summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/encode
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2019-08-21 11:28:03 -0600
committerGitHub <noreply@github.com>2019-08-21 11:28:03 -0600
commit0544f0266a11874efb305a46db762248e64bc62d (patch)
tree3e75e6f59047bb967c52915e1e75aee6ad67c0ea /modules/caddyhttp/encode
parent42f75a4ca94ef3fb5e15a74e5dc9ef8b4f1f0b39 (diff)
parentb2aa679c33f63ebec5bc1a21bca01f345dffebdd (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.go36
-rw-r--r--modules/caddyhttp/encode/caddyfile.go99
-rw-r--r--modules/caddyhttp/encode/encode.go45
-rw-r--r--modules/caddyhttp/encode/gzip/gzip.go38
-rw-r--r--modules/caddyhttp/encode/zstd/zstd.go26
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)
+)