-
Notifications
You must be signed in to change notification settings - Fork 56
/
compress.go
117 lines (101 loc) · 2.84 KB
/
compress.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package gear
import (
"compress/gzip"
"compress/zlib"
"io"
"net/http"
)
// Compressible interface is use to enable compress response content.
type Compressible interface {
// Compressible checks the response Content-Type and Content-Length to
// determine whether to compress.
// `length == 0` means response body maybe stream, or will be writed later.
Compressible(contentType string, contentLength int) bool
}
// DefaultCompress is defalut Compress implemented. Use it to enable compress:
//
// app.Set(gear.SetCompress, &gear.DefaultCompress{})
type DefaultCompress struct{}
// Compressible implemented Compress interface.
func (d *DefaultCompress) Compressible(contentType string, contentLength int) bool {
if contentLength > 0 && contentLength <= 1024 {
return false
}
return contentType != ""
}
// ThresholdCompress is an impelementation with transhold. The transhold defines the // minimun content length to enable compressible check.
type ThresholdCompress int
// Compressible implemented Compress interface.
//
// app := gear.New()
// app.Set(gear.SetCompress, gear.ThresholdCompress(128))
//
// // Add a static middleware
// app.Use(static.New(static.Options{
// Root: "./",
// Prefix: "/",
// }))
// app.Error(app.Listen(":3000")) // http://127.0.0.1:3000/
func (tc ThresholdCompress) Compressible(contentType string, contentLength int) bool {
if contentLength < int(tc) {
return false
}
return contentType != ""
}
// http.ResponseWriter wrapper
type compressWriter struct {
compress Compressible
encoding string
writer io.WriteCloser
res *Response
rw http.ResponseWriter // underlying http.ResponseWriter
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
func newCompress(res *Response, c Compressible, encoding string) *compressWriter {
switch encoding {
case "gzip", "deflate":
return &compressWriter{
compress: c,
res: res,
rw: res.rw,
encoding: encoding,
}
default:
return nil
}
}
func (cw *compressWriter) WriteHeader(code int) {
defer cw.rw.WriteHeader(code)
if !isEmptyStatus(code) &&
cw.compress.Compressible(cw.res.Get(HeaderContentType), len(cw.res.body)) {
var w io.WriteCloser
// http://www.gzip.org/zlib/zlib_faq.html#faq38
switch cw.encoding {
case "gzip": // recommend
w = gzip.NewWriter(cw.rw)
case "deflate": // should be zlib
w = zlib.NewWriter(cw.rw)
}
if w != nil {
cw.writer = w
cw.res.Del(HeaderContentLength)
cw.res.Set(HeaderContentEncoding, cw.encoding)
cw.res.Vary(HeaderAcceptEncoding)
}
}
}
func (cw *compressWriter) Header() http.Header {
return cw.rw.Header()
}
func (cw *compressWriter) Write(b []byte) (int, error) {
if cw.writer != nil {
return cw.writer.Write(b)
}
return cw.rw.Write(b)
}
func (cw *compressWriter) Close() error {
if cw.writer != nil {
return cw.writer.Close()
}
return nil
}