diff options
author | Matt Holt <mholt@users.noreply.github.com> | 2019-08-21 11:28:03 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-21 11:28:03 -0600 |
commit | 0544f0266a11874efb305a46db762248e64bc62d (patch) | |
tree | 3e75e6f59047bb967c52915e1e75aee6ad67c0ea /modules/caddyhttp/encode | |
parent | 42f75a4ca94ef3fb5e15a74e5dc9ef8b4f1f0b39 (diff) | |
parent | b2aa679c33f63ebec5bc1a21bca01f345dffebdd (diff) |
Merge pull request #2699 from caddyserver/cfadapter
v2: Implement config adapters and WIP Caddyfile adapter
Diffstat (limited to 'modules/caddyhttp/encode')
-rw-r--r-- | modules/caddyhttp/encode/brotli/brotli.go | 36 | ||||
-rw-r--r-- | modules/caddyhttp/encode/caddyfile.go | 99 | ||||
-rw-r--r-- | modules/caddyhttp/encode/encode.go | 45 | ||||
-rw-r--r-- | modules/caddyhttp/encode/gzip/gzip.go | 38 | ||||
-rw-r--r-- | modules/caddyhttp/encode/zstd/zstd.go | 26 |
5 files changed, 212 insertions, 32 deletions
diff --git a/modules/caddyhttp/encode/brotli/brotli.go b/modules/caddyhttp/encode/brotli/brotli.go index 0890d43..cf055aa 100644 --- a/modules/caddyhttp/encode/brotli/brotli.go +++ b/modules/caddyhttp/encode/brotli/brotli.go @@ -16,17 +16,16 @@ package caddybrotli import ( "fmt" + "strconv" "github.com/andybalholm/brotli" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode" ) func init() { - caddy.RegisterModule(caddy.Module{ - Name: "http.encoders.brotli", - New: func() interface{} { return new(Brotli) }, - }) + caddy.RegisterModule(Brotli{}) } // Brotli can create brotli encoders. Note that brotli @@ -35,6 +34,30 @@ type Brotli struct { Quality *int `json:"quality,omitempty"` } +// CaddyModule returns the Caddy module information. +func (Brotli) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + Name: "http.encoders.brotli", + New: func() caddy.Module { return new(Brotli) }, + } +} + +// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. +func (b *Brotli) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + for d.Next() { + if !d.NextArg() { + continue + } + qualityStr := d.Val() + quality, err := strconv.Atoi(qualityStr) + if err != nil { + return err + } + b.Quality = &quality + } + return nil +} + // Validate validates b's configuration. func (b Brotli) Validate() error { if b.Quality != nil { @@ -64,6 +87,7 @@ func (b Brotli) NewEncoder() encode.Encoder { // Interface guards var ( - _ encode.Encoding = (*Brotli)(nil) - _ caddy.Validator = (*Brotli)(nil) + _ encode.Encoding = (*Brotli)(nil) + _ caddy.Validator = (*Brotli)(nil) + _ caddyfile.Unmarshaler = (*Brotli)(nil) ) diff --git a/modules/caddyhttp/encode/caddyfile.go b/modules/caddyhttp/encode/caddyfile.go new file mode 100644 index 0000000..5762bd3 --- /dev/null +++ b/modules/caddyhttp/encode/caddyfile.go @@ -0,0 +1,99 @@ +// 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 encode + +import ( + "encoding/json" + "fmt" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" + "github.com/caddyserver/caddy/v2/modules/caddyhttp" +) + +func init() { + httpcaddyfile.RegisterHandlerDirective("encode", parseCaddyfile) +} + +// TODO: This is a good example of why UnmarshalCaddyfile is still a good idea... hmm. +func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { + enc := new(Encode) + err := enc.UnmarshalCaddyfile(h.Dispenser) + if err != nil { + return nil, err + } + return enc, nil +} + +// TODO: Keep UnmarshalCaddyfile pattern? + +// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax: +// +// encode [<matcher>] <formats...> { +// gzip [<level>] +// zstd +// brotli [<quality>] +// } +// +// Specifying the formats on the first line will use those formats' defaults. +func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + 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.Name, err) + } + encoding, ok := mod.New().(Encoding) + if !ok { + return fmt.Errorf("module %s is not an HTTP encoding", mod.Name) + } + if enc.EncodingsRaw == nil { + enc.EncodingsRaw = make(map[string]json.RawMessage) + } + enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil) + } + + for d.NextBlock() { + name := d.Val() + mod, err := caddy.GetModule("http.encoders." + name) + if err != nil { + return fmt.Errorf("getting encoder module '%s': %v", mod.Name, err) + } + unm, ok := mod.New().(caddyfile.Unmarshaler) + if !ok { + return fmt.Errorf("encoder module '%s' is not a Caddyfile unmarshaler", mod.Name) + } + err = unm.UnmarshalCaddyfile(d.NewFromNextTokens()) + if err != nil { + return err + } + encoding, ok := unm.(Encoding) + if !ok { + return fmt.Errorf("module %s is not an HTTP encoding", mod.Name) + } + if enc.EncodingsRaw == nil { + enc.EncodingsRaw = make(map[string]json.RawMessage) + } + enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil) + } + } + + return nil +} + +// Interface guard +var _ caddyfile.Unmarshaler = (*Encode)(nil) diff --git a/modules/caddyhttp/encode/encode.go b/modules/caddyhttp/encode/encode.go index b2c1327..723b988 100644 --- a/modules/caddyhttp/encode/encode.go +++ b/modules/caddyhttp/encode/encode.go @@ -35,10 +35,7 @@ import ( ) func init() { - caddy.RegisterModule(caddy.Module{ - Name: "http.handlers.encode", - New: func() interface{} { return new(Encode) }, - }) + caddy.RegisterModule(Encode{}) } // Encode is a middleware which can encode responses. @@ -50,21 +47,25 @@ type Encode struct { writerPools map[string]*sync.Pool // TODO: these pools do not get reused through config reloads... } +// CaddyModule returns the Caddy module information. +func (Encode) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + Name: "http.handlers.encode", + New: func() caddy.Module { return new(Encode) }, + } +} + // Provision provisions enc. func (enc *Encode) Provision(ctx caddy.Context) error { - enc.writerPools = make(map[string]*sync.Pool) - for modName, rawMsg := range enc.EncodingsRaw { val, err := ctx.LoadModule("http.encoders."+modName, rawMsg) if err != nil { return fmt.Errorf("loading encoder module '%s': %v", modName, err) } - encoder := val.(Encoding) - - enc.writerPools[encoder.AcceptEncoding()] = &sync.Pool{ - New: func() interface{} { - return encoder.NewEncoder() - }, + encoding := val.(Encoding) + err = enc.addEncoding(encoding) + if err != nil { + return err } } enc.EncodingsRaw = nil // allow GC to deallocate - TODO: Does this help? @@ -85,10 +86,28 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh defer w.(*responseWriter).Close() break } - return next.ServeHTTP(w, r) } +func (enc *Encode) addEncoding(e Encoding) error { + ae := e.AcceptEncoding() + if ae == "" { + return fmt.Errorf("encoder does not specify an Accept-Encoding value") + } + if _, ok := enc.writerPools[ae]; ok { + return fmt.Errorf("encoder already added: %s", ae) + } + if enc.writerPools == nil { + enc.writerPools = make(map[string]*sync.Pool) + } + enc.writerPools[ae] = &sync.Pool{ + New: func() interface{} { + return e.NewEncoder() + }, + } + return nil +} + // openResponseWriter creates a new response writer that may (or may not) // encode the response with encodingName. The returned response writer MUST // be closed after the handler completes. diff --git a/modules/caddyhttp/encode/gzip/gzip.go b/modules/caddyhttp/encode/gzip/gzip.go index 45c5f54..d6d67f7 100644 --- a/modules/caddyhttp/encode/gzip/gzip.go +++ b/modules/caddyhttp/encode/gzip/gzip.go @@ -18,16 +18,15 @@ import ( "compress/flate" "compress/gzip" // TODO: consider using https://github.com/klauspost/compress/gzip "fmt" + "strconv" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode" ) func init() { - caddy.RegisterModule(caddy.Module{ - Name: "http.encoders.gzip", - New: func() interface{} { return new(Gzip) }, - }) + caddy.RegisterModule(Gzip{}) } // Gzip can create gzip encoders. @@ -35,6 +34,30 @@ type Gzip struct { Level int `json:"level,omitempty"` } +// CaddyModule returns the Caddy module information. +func (Gzip) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + Name: "http.encoders.gzip", + New: func() caddy.Module { return new(Gzip) }, + } +} + +// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. +func (g *Gzip) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + for d.Next() { + if !d.NextArg() { + continue + } + levelStr := d.Val() + level, err := strconv.Atoi(levelStr) + if err != nil { + return err + } + g.Level = level + } + return nil +} + // Provision provisions g's configuration. func (g *Gzip) Provision(ctx caddy.Context) error { if g.Level == 0 { @@ -69,7 +92,8 @@ var defaultGzipLevel = 5 // Interface guards var ( - _ encode.Encoding = (*Gzip)(nil) - _ caddy.Provisioner = (*Gzip)(nil) - _ caddy.Validator = (*Gzip)(nil) + _ encode.Encoding = (*Gzip)(nil) + _ caddy.Provisioner = (*Gzip)(nil) + _ caddy.Validator = (*Gzip)(nil) + _ caddyfile.Unmarshaler = (*Gzip)(nil) ) diff --git a/modules/caddyhttp/encode/zstd/zstd.go b/modules/caddyhttp/encode/zstd/zstd.go index acebff5..f2b4e85 100644 --- a/modules/caddyhttp/encode/zstd/zstd.go +++ b/modules/caddyhttp/encode/zstd/zstd.go @@ -16,20 +16,31 @@ package caddyzstd import ( "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode" "github.com/klauspost/compress/zstd" ) func init() { - caddy.RegisterModule(caddy.Module{ - Name: "http.encoders.zstd", - New: func() interface{} { return new(Zstd) }, - }) + caddy.RegisterModule(Zstd{}) } // Zstd can create Zstandard encoders. type Zstd struct{} +// CaddyModule returns the Caddy module information. +func (Zstd) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + Name: "http.encoders.zstd", + New: func() caddy.Module { return new(Zstd) }, + } +} + +// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. +func (z *Zstd) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + return nil +} + // AcceptEncoding returns the name of the encoding as // used in the Accept-Encoding request headers. func (Zstd) AcceptEncoding() string { return "zstd" } @@ -40,5 +51,8 @@ func (z Zstd) NewEncoder() encode.Encoder { return writer } -// Interface guard -var _ encode.Encoding = (*Zstd)(nil) +// Interface guards +var ( + _ encode.Encoding = (*Zstd)(nil) + _ caddyfile.Unmarshaler = (*Zstd)(nil) +) |