use go 1.12

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2019-03-12 15:47:56 +08:00
parent b59c244ca2
commit 4144404b0b
1110 changed files with 161100 additions and 14519 deletions

163
vendor/github.com/mholt/caddy/caddyhttp/gzip/gzip.go generated vendored Normal file
View File

@@ -0,0 +1,163 @@
// Copyright 2015 Light Code Labs, LLC
//
// 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 gzip provides a middleware layer that performs
// gzip compression on the response.
package gzip
import (
"compress/gzip"
"io"
"net/http"
"strings"
"github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
func init() {
caddy.RegisterPlugin("gzip", caddy.Plugin{
ServerType: "http",
Action: setup,
})
initWriterPool()
}
// Gzip is a middleware type which gzips HTTP responses. It is
// imperative that any handler which writes to a gzipped response
// specifies the Content-Type, otherwise some clients will assume
// application/x-gzip and try to download a file.
type Gzip struct {
Next httpserver.Handler
Configs []Config
}
// Config holds the configuration for Gzip middleware
type Config struct {
RequestFilters []RequestFilter
ResponseFilters []ResponseFilter
Level int // Compression level
}
// ServeHTTP serves a gzipped response if the client supports it.
func (g Gzip) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
return g.Next.ServeHTTP(w, r)
}
outer:
for _, c := range g.Configs {
// Check request filters to determine if gzipping is permitted for this request
for _, filter := range c.RequestFilters {
if !filter.ShouldCompress(r) {
continue outer
}
}
// In order to avoid unused memory allocation, gzip.putWriter only be called when gzip compression happened.
// see https://github.com/mholt/caddy/issues/2395
gz := &gzipResponseWriter{
ResponseWriterWrapper: &httpserver.ResponseWriterWrapper{ResponseWriter: w},
newWriter: func() io.Writer {
// gzipWriter modifies underlying writer at init,
// use a discard writer instead to leave ResponseWriter in
// original form.
return getWriter(c.Level)
},
}
defer func() {
if gzWriter, ok := gz.internalWriter.(*gzip.Writer); ok {
putWriter(c.Level, gzWriter)
}
}()
var rw http.ResponseWriter
// if no response filter is used
if len(c.ResponseFilters) == 0 {
// replace discard writer with ResponseWriter
if gzWriter, ok := gz.Writer().(*gzip.Writer); ok {
gzWriter.Reset(w)
}
rw = gz
} else {
// wrap gzip writer with ResponseFilterWriter
rw = NewResponseFilterWriter(c.ResponseFilters, gz)
}
// Any response in forward middleware will now be compressed
status, err := g.Next.ServeHTTP(rw, r)
// If there was an error that remained unhandled, we need
// to send something back before gzipWriter gets closed at
// the return of this method!
if status >= 400 {
httpserver.DefaultErrorFunc(w, r, status)
return 0, err
}
return status, err
}
// no matching filter
return g.Next.ServeHTTP(w, r)
}
// gzipResponseWriter wraps the underlying Write method
// with a gzip.Writer to compress the output.
type gzipResponseWriter struct {
internalWriter io.Writer
*httpserver.ResponseWriterWrapper
statusCodeWritten bool
newWriter func() io.Writer
}
// WriteHeader wraps the underlying WriteHeader method to prevent
// problems with conflicting headers from proxied backends. For
// example, a backend system that calculates Content-Length would
// be wrong because it doesn't know it's being gzipped.
func (w *gzipResponseWriter) WriteHeader(code int) {
w.Header().Del("Content-Length")
w.Header().Set("Content-Encoding", "gzip")
w.Header().Add("Vary", "Accept-Encoding")
originalEtag := w.Header().Get("ETag")
if originalEtag != "" && !strings.HasPrefix(originalEtag, "W/") {
w.Header().Set("ETag", "W/"+originalEtag)
}
w.ResponseWriterWrapper.WriteHeader(code)
w.statusCodeWritten = true
}
// Write wraps the underlying Write method to do compression.
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
if w.Header().Get("Content-Type") == "" {
w.Header().Set("Content-Type", http.DetectContentType(b))
}
if !w.statusCodeWritten {
w.WriteHeader(http.StatusOK)
}
n, err := w.Writer().Write(b)
return n, err
}
//Writer use a lazy way to initialize Writer
func (w *gzipResponseWriter) Writer() io.Writer {
if w.internalWriter == nil {
w.internalWriter = w.newWriter()
}
return w.internalWriter
}
// Interface guards
var _ httpserver.HTTPInterfaces = (*gzipResponseWriter)(nil)

View File

@@ -0,0 +1,105 @@
// Copyright 2015 Light Code Labs, LLC
//
// 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 gzip
import (
"net/http"
"path"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
// RequestFilter determines if a request should be gzipped.
type RequestFilter interface {
// ShouldCompress tells if gzip compression
// should be done on the request.
ShouldCompress(*http.Request) bool
}
// defaultExtensions is the list of default extensions for which to enable gzipping.
var defaultExtensions = []string{"", ".txt", ".htm", ".html", ".css", ".php", ".js", ".json",
".md", ".mdown", ".xml", ".svg", ".go", ".cgi", ".py", ".pl", ".aspx", ".asp", ".m3u", ".m3u8"}
// DefaultExtFilter creates an ExtFilter with default extensions.
func DefaultExtFilter() ExtFilter {
m := ExtFilter{Exts: make(Set)}
for _, extension := range defaultExtensions {
m.Exts.Add(extension)
}
return m
}
// ExtFilter is RequestFilter for file name extensions.
type ExtFilter struct {
// Exts is the file name extensions to accept
Exts Set
}
// ExtWildCard is the wildcard for extensions.
const ExtWildCard = "*"
// ShouldCompress checks if the request file extension matches any
// of the registered extensions. It returns true if the extension is
// found and false otherwise.
func (e ExtFilter) ShouldCompress(r *http.Request) bool {
ext := path.Ext(r.URL.Path)
return e.Exts.Contains(ExtWildCard) || e.Exts.Contains(ext)
}
// PathFilter is RequestFilter for request path.
type PathFilter struct {
// IgnoredPaths is the paths to ignore
IgnoredPaths Set
}
// ShouldCompress checks if the request path matches any of the
// registered paths to ignore. It returns false if an ignored path
// is found and true otherwise.
func (p PathFilter) ShouldCompress(r *http.Request) bool {
return !p.IgnoredPaths.ContainsFunc(func(value string) bool {
return httpserver.Path(r.URL.Path).Matches(value)
})
}
// Set stores distinct strings.
type Set map[string]struct{}
// Add adds an element to the set.
func (s Set) Add(value string) {
s[value] = struct{}{}
}
// Remove removes an element from the set.
func (s Set) Remove(value string) {
delete(s, value)
}
// Contains check if the set contains value.
func (s Set) Contains(value string) bool {
_, ok := s[value]
return ok
}
// ContainsFunc is similar to Contains. It iterates all the
// elements in the set and passes each to f. It returns true
// on the first call to f that returns true and false otherwise.
func (s Set) ContainsFunc(f func(string) bool) bool {
for k := range s {
if f(k) {
return true
}
}
return false
}

View File

@@ -0,0 +1,107 @@
// Copyright 2015 Light Code Labs, LLC
//
// 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 gzip
import (
"compress/gzip"
"net/http"
"strconv"
)
// ResponseFilter determines if the response should be gzipped.
type ResponseFilter interface {
ShouldCompress(http.ResponseWriter) bool
}
// LengthFilter is ResponseFilter for minimum content length.
type LengthFilter int64
// ShouldCompress returns if content length is greater than or
// equals to minimum length.
func (l LengthFilter) ShouldCompress(w http.ResponseWriter) bool {
contentLength := w.Header().Get("Content-Length")
length, err := strconv.ParseInt(contentLength, 10, 64)
if err != nil || length == 0 {
return false
}
return l != 0 && int64(l) <= length
}
// SkipCompressedFilter is ResponseFilter that will discard already compressed responses
type SkipCompressedFilter struct{}
// ShouldCompress returns true if served file is not already compressed
// encodings via https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
func (n SkipCompressedFilter) ShouldCompress(w http.ResponseWriter) bool {
switch w.Header().Get("Content-Encoding") {
case "gzip", "compress", "deflate", "br":
return false
default:
return true
}
}
// ResponseFilterWriter validates ResponseFilters. It writes
// gzip compressed data if ResponseFilters are satisfied or
// uncompressed data otherwise.
type ResponseFilterWriter struct {
filters []ResponseFilter
shouldCompress bool
statusCodeWritten bool
*gzipResponseWriter
}
// NewResponseFilterWriter creates and initializes a new ResponseFilterWriter.
func NewResponseFilterWriter(filters []ResponseFilter, gz *gzipResponseWriter) *ResponseFilterWriter {
return &ResponseFilterWriter{filters: filters, gzipResponseWriter: gz}
}
// WriteHeader wraps underlying WriteHeader method and
// compresses if filters are satisfied.
func (r *ResponseFilterWriter) WriteHeader(code int) {
// Determine if compression should be used or not.
r.shouldCompress = true
for _, filter := range r.filters {
if !filter.ShouldCompress(r) {
r.shouldCompress = false
break
}
}
if r.shouldCompress {
// replace discard writer with ResponseWriter
if gzWriter, ok := r.gzipResponseWriter.Writer().(*gzip.Writer); ok {
gzWriter.Reset(r.ResponseWriter)
}
// use gzip WriteHeader to include and delete
// necessary headers
r.gzipResponseWriter.WriteHeader(code)
} else {
r.ResponseWriter.WriteHeader(code)
}
r.statusCodeWritten = true
}
// Write wraps underlying Write method and compresses if filters
// are satisfied
func (r *ResponseFilterWriter) Write(b []byte) (int, error) {
if !r.statusCodeWritten {
r.WriteHeader(http.StatusOK)
}
if r.shouldCompress {
return r.gzipResponseWriter.Write(b)
}
return r.ResponseWriter.Write(b)
}

183
vendor/github.com/mholt/caddy/caddyhttp/gzip/setup.go generated vendored Normal file
View File

@@ -0,0 +1,183 @@
// Copyright 2015 Light Code Labs, LLC
//
// 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 gzip
import (
"compress/gzip"
"fmt"
"io/ioutil"
"strconv"
"strings"
"sync"
"github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
// setup configures a new gzip middleware instance.
func setup(c *caddy.Controller) error {
configs, err := gzipParse(c)
if err != nil {
return err
}
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
return Gzip{Next: next, Configs: configs}
})
return nil
}
func gzipParse(c *caddy.Controller) ([]Config, error) {
var configs []Config
for c.Next() {
config := Config{}
// Request Filters
pathFilter := PathFilter{IgnoredPaths: make(Set)}
extFilter := ExtFilter{Exts: make(Set)}
// Response Filters
lengthFilter := LengthFilter(0)
// No extra args expected
if len(c.RemainingArgs()) > 0 {
return configs, c.ArgErr()
}
for c.NextBlock() {
switch c.Val() {
case "ext":
exts := c.RemainingArgs()
if len(exts) == 0 {
return configs, c.ArgErr()
}
for _, e := range exts {
if !strings.HasPrefix(e, ".") && e != ExtWildCard && e != "" {
return configs, fmt.Errorf(`gzip: invalid extension "%v" (must start with dot)`, e)
}
extFilter.Exts.Add(e)
}
case "not":
paths := c.RemainingArgs()
if len(paths) == 0 {
return configs, c.ArgErr()
}
for _, p := range paths {
if p == "/" {
return configs, fmt.Errorf(`gzip: cannot exclude path "/" - remove directive entirely instead`)
}
if !strings.HasPrefix(p, "/") {
return configs, fmt.Errorf(`gzip: invalid path "%v" (must start with /)`, p)
}
pathFilter.IgnoredPaths.Add(p)
}
case "level":
if !c.NextArg() {
return configs, c.ArgErr()
}
level, _ := strconv.Atoi(c.Val())
config.Level = level
case "min_length":
if !c.NextArg() {
return configs, c.ArgErr()
}
length, err := strconv.ParseInt(c.Val(), 10, 64)
if err != nil {
return configs, err
} else if length == 0 {
return configs, fmt.Errorf(`gzip: min_length must be greater than 0`)
}
lengthFilter = LengthFilter(length)
default:
return configs, c.ArgErr()
}
}
// Request Filters
config.RequestFilters = []RequestFilter{}
// If ignored paths are specified, put in front to filter with path first
if len(pathFilter.IgnoredPaths) > 0 {
config.RequestFilters = []RequestFilter{pathFilter}
}
// Then, if extensions are specified, use those to filter.
// Otherwise, use default extensions filter.
if len(extFilter.Exts) > 0 {
config.RequestFilters = append(config.RequestFilters, extFilter)
} else {
config.RequestFilters = append(config.RequestFilters, DefaultExtFilter())
}
config.ResponseFilters = append(config.ResponseFilters, SkipCompressedFilter{})
// Response Filters
// If min_length is specified, use it.
if int64(lengthFilter) != 0 {
config.ResponseFilters = append(config.ResponseFilters, lengthFilter)
}
configs = append(configs, config)
}
return configs, nil
}
// pool gzip.Writer according to compress level
// so we can reuse allocations over time
var (
writerPool = map[int]*sync.Pool{}
defaultWriterPoolIndex int
)
func initWriterPool() {
var i int
newWriterPool := func(level int) *sync.Pool {
return &sync.Pool{
New: func() interface{} {
w, _ := gzip.NewWriterLevel(ioutil.Discard, level)
return w
},
}
}
for i = gzip.BestSpeed; i <= gzip.BestCompression; i++ {
writerPool[i] = newWriterPool(i)
}
// add default writer pool
defaultWriterPoolIndex = i
writerPool[defaultWriterPoolIndex] = newWriterPool(gzip.DefaultCompression)
}
func getWriter(level int) *gzip.Writer {
index := defaultWriterPoolIndex
if level >= gzip.BestSpeed && level <= gzip.BestCompression {
index = level
}
w := writerPool[index].Get().(*gzip.Writer)
w.Reset(ioutil.Discard)
return w
}
func putWriter(level int, w *gzip.Writer) {
index := defaultWriterPoolIndex
if level >= gzip.BestSpeed && level <= gzip.BestCompression {
index = level
}
w.Close()
writerPool[index].Put(w)
}