@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 02:15 UTC

master
go 168 lines 4.27 KB
Raw
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 caddyzstd
16
17 import (
18 "fmt"
19
20 "github.com/klauspost/compress/zstd"
21
22 "github.com/caddyserver/caddy/v2"
23 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
24 "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode"
25 )
26
27 func init() {
28 caddy.RegisterModule(Zstd{})
29 }
30
31 // Zstd can create Zstandard encoders.
32 type Zstd struct {
33 // The compression level. Accepted values: fastest, better, best, default.
34 Level string `json:"level,omitempty"`
35
36 // Whether to include the optional 4-byte zstd frame checksum trailer.
37 // If unset, the upstream zstd library default is preserved.
38 Checksum *bool `json:"checksum,omitempty"`
39
40 // Compression level refer to type constants value from zstd.SpeedFastest to zstd.SpeedBestCompression
41 level zstd.EncoderLevel
42 }
43
44 // CaddyModule returns the Caddy module information.
45 func (Zstd) CaddyModule() caddy.ModuleInfo {
46 return caddy.ModuleInfo{
47 ID: "http.encoders.zstd",
48 New: func() caddy.Module { return new(Zstd) },
49 }
50 }
51
52 // UnmarshalCaddyfile sets up the handler from Caddyfile tokens.
53 func (z *Zstd) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
54 d.Next() // consume option name
55 args := d.RemainingArgs()
56 switch len(args) {
57 case 0:
58 case 1:
59 if _, err := parseEncoderLevel(args[0]); err != nil {
60 return d.Err(err.Error())
61 }
62 z.Level = args[0]
63 default:
64 return d.ArgErr()
65 }
66
67 for nesting := d.Nesting(); d.NextBlock(nesting); {
68 switch d.Val() {
69 case "level":
70 args := d.RemainingArgs()
71 if len(args) != 1 {
72 return d.ArgErr()
73 }
74 if z.Level != "" {
75 return d.Err("compression level already specified")
76 }
77 if _, err := parseEncoderLevel(args[0]); err != nil {
78 return d.Err(err.Error())
79 }
80 z.Level = args[0]
81
82 case "disable_checksum":
83 if d.NextArg() {
84 return d.ArgErr()
85 }
86 if z.Checksum != nil {
87 return d.Err("checksum already specified")
88 }
89 disabled := false
90 z.Checksum = &disabled
91
92 default:
93 return d.Errf("unknown subdirective '%s'", d.Val())
94 }
95 }
96
97 return nil
98 }
99
100 // Provision provisions z's configuration.
101 func (z *Zstd) Provision(ctx caddy.Context) error {
102 if z.Level == "" {
103 z.Level = zstd.SpeedDefault.String()
104 }
105 level, err := parseEncoderLevel(z.Level)
106 if err != nil {
107 return err
108 }
109 z.level = level
110 return nil
111 }
112
113 // AcceptEncoding returns the name of the encoding as
114 // used in the Accept-Encoding request headers.
115 func (Zstd) AcceptEncoding() string { return "zstd" }
116
117 // NewEncoder returns a new Zstandard writer.
118 func (z Zstd) NewEncoder() encode.Encoder {
119 // The default of 8MB for the window is
120 // too large for many clients, so we limit
121 // it to 128K to lighten their load.
122 writer, _ := zstd.NewWriter(nil, z.writerOptions(128<<10)...)
123 return writer
124 }
125
126 func (z Zstd) writerOptions(windowSize int) []zstd.EOption {
127 opts := []zstd.EOption{
128 zstd.WithWindowSize(windowSize),
129 zstd.WithEncoderConcurrency(1),
130 zstd.WithZeroFrames(true),
131 zstd.WithEncoderLevel(z.encoderLevel()),
132 }
133 if z.Checksum != nil {
134 opts = append(opts, zstd.WithEncoderCRC(*z.Checksum))
135 }
136 return opts
137 }
138
139 func (z Zstd) encoderLevel() zstd.EncoderLevel {
140 if z.level != 0 {
141 return z.level
142 }
143 if z.Level != "" {
144 if level, err := parseEncoderLevel(z.Level); err == nil {
145 return level
146 }
147 }
148 return zstd.SpeedDefault
149 }
150
151 func parseEncoderLevel(level string) (zstd.EncoderLevel, error) {
152 if ok, encLevel := zstd.EncoderLevelFromString(level); ok {
153 return encLevel, nil
154 }
155 return 0, fmt.Errorf("unexpected compression level, use one of '%s', '%s', '%s', '%s'",
156 zstd.SpeedFastest,
157 zstd.SpeedBetterCompression,
158 zstd.SpeedBestCompression,
159 zstd.SpeedDefault,
160 )
161 }
162
163 // Interface guards
164 var (
165 _ encode.Encoding = (*Zstd)(nil)
166 _ caddyfile.Unmarshaler = (*Zstd)(nil)
167 _ caddy.Provisioner = (*Zstd)(nil)
168 )