summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/NYTimes/gziphandler
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-09-23 10:17:51 -0400
committerGitHub <noreply@github.com>2016-09-23 10:17:51 -0400
commit2ca0e8f9a0f9863555a26e984cde15efff9ef8f8 (patch)
treedaae1ee67b14a3d0a84424f2a304885d9e75ce2b /vendor/github.com/NYTimes/gziphandler
parent6d62d65b2dc85855aabea036cbd44f6059e19d13 (diff)
downloadchat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.gz
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.bz2
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.zip
Updating golang dependancies (#4075)
Diffstat (limited to 'vendor/github.com/NYTimes/gziphandler')
-rw-r--r--vendor/github.com/NYTimes/gziphandler/.gitignore1
-rw-r--r--vendor/github.com/NYTimes/gziphandler/.travis.yml5
-rw-r--r--vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md75
-rw-r--r--vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md30
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip.go170
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip_test.go184
6 files changed, 410 insertions, 55 deletions
diff --git a/vendor/github.com/NYTimes/gziphandler/.gitignore b/vendor/github.com/NYTimes/gziphandler/.gitignore
new file mode 100644
index 000000000..1377554eb
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/.gitignore
@@ -0,0 +1 @@
+*.swp
diff --git a/vendor/github.com/NYTimes/gziphandler/.travis.yml b/vendor/github.com/NYTimes/gziphandler/.travis.yml
new file mode 100644
index 000000000..f918f8d76
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/.travis.yml
@@ -0,0 +1,5 @@
+language: go
+
+go:
+ - 1.7
+ - tip
diff --git a/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..cdbca194c
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md
@@ -0,0 +1,75 @@
+---
+layout: code-of-conduct
+version: v1.0
+---
+
+This code of conduct outlines our expectations for participants within the **NYTimes/gziphandler** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community.
+
+Our open source community strives to:
+
+* **Be friendly and patient.**
+* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
+* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
+* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one.
+* **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable.
+* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
+
+## Definitions
+
+Harassment includes, but is not limited to:
+
+- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation
+- Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment
+- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle
+- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop
+- Threats of violence, both physical and psychological
+- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm
+- Deliberate intimidation
+- Stalking or following
+- Harassing photography or recording, including logging online activity for harassment purposes
+- Sustained disruption of discussion
+- Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour
+- Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others
+- Continued one-on-one communication after requests to cease
+- Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect others from intentional abuse
+- Publication of non-harassing private communication
+
+Our open source community prioritizes marginalized people’s safety over privileged people’s comfort. We will not act on complaints regarding:
+
+- ‘Reverse’ -isms, including ‘reverse racism,’ ‘reverse sexism,’ and ‘cisphobia’
+- Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you”
+- Refusal to explain or debate social justice concepts
+- Communicating in a ‘tone’ you don’t find congenial
+- Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions
+
+
+### Diversity Statement
+
+We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
+
+Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected
+characteristics above, including participants with disabilities.
+
+### Reporting Issues
+
+If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via **code@nytimes.com**. All reports will be handled with discretion. In your report please include:
+
+- Your contact information.
+- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please
+include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link.
+- Any additional information that may be helpful.
+
+After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse.
+
+### Attribution & Acknowledgements
+
+We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration:
+
+* [Django](https://www.djangoproject.com/conduct/reporting/)
+* [Python](https://www.python.org/community/diversity/)
+* [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct)
+* [Contributor Covenant](http://contributor-covenant.org/)
+* [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/)
+* [Citizen Code of Conduct](http://citizencodeofconduct.org/)
+
+This Code of Conduct was based on https://github.com/todogroup/opencodeofconduct
diff --git a/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md
new file mode 100644
index 000000000..b89a9eb4f
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# Contributing to NYTimes/gziphandler
+
+This is an open source project started by handful of developers at The New York Times and open to the entire Go community.
+
+We really appreciate your help!
+
+## Filing issues
+
+When filing an issue, make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+## Contributing code
+
+Before submitting changes, please follow these guidelines:
+
+1. Check the open issues and pull requests for existing discussions.
+2. Open an issue to discuss a new feature.
+3. Write tests.
+4. Make sure code follows the ['Go Code Review Comments'](https://github.com/golang/go/wiki/CodeReviewComments).
+5. Make sure your changes pass `go test`.
+6. Make sure the entire test suite passes locally and on Travis CI.
+7. Open a Pull Request.
+8. [Squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) after receiving feedback and add a [great commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
+
+Unless otherwise noted, the gziphandler source files are distributed under the Apache 2.0-style license found in the LICENSE.md file.
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go
index ee1526f42..39e8c5e75 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip.go
@@ -13,6 +13,8 @@ const (
vary = "Vary"
acceptEncoding = "Accept-Encoding"
contentEncoding = "Content-Encoding"
+ contentType = "Content-Type"
+ contentLength = "Content-Length"
)
type codings map[string]float64
@@ -22,57 +24,160 @@ type codings map[string]float64
// The examples seem to indicate that it is.
const DEFAULT_QVALUE = 1.0
-var gzipWriterPool = sync.Pool{
- New: func() interface{} { return gzip.NewWriter(nil) },
+// gzipWriterPools stores a sync.Pool for each compression level for reuse of
+// gzip.Writers. Use poolIndex to covert a compression level to an index into
+// gzipWriterPools.
+var gzipWriterPools [gzip.BestCompression - gzip.BestSpeed + 2]*sync.Pool
+
+func init() {
+ for i := gzip.BestSpeed; i <= gzip.BestCompression; i++ {
+ addLevelPool(i)
+ }
+ addLevelPool(gzip.DefaultCompression)
+}
+
+// poolIndex maps a compression level to its index into gzipWriterPools. It
+// assumes that level is a valid gzip compression level.
+func poolIndex(level int) int {
+ // gzip.DefaultCompression == -1, so we need to treat it special.
+ if level == gzip.DefaultCompression {
+ return gzip.BestCompression - gzip.BestSpeed + 1
+ }
+ return level - gzip.BestSpeed
+}
+
+func addLevelPool(level int) {
+ gzipWriterPools[poolIndex(level)] = &sync.Pool{
+ New: func() interface{} {
+ // NewWriterLevel only returns error on a bad level, we are guaranteeing
+ // that this will be a valid level so it is okay to ignore the returned
+ // error.
+ w, _ := gzip.NewWriterLevel(nil, level)
+ return w
+ },
+ }
}
// GzipResponseWriter provides an http.ResponseWriter interface, which gzips
-// bytes before writing them to the underlying response. This doesn't set the
-// Content-Encoding header, nor close the writers, so don't forget to do that.
+// bytes before writing them to the underlying response. This doesn't close the
+// writers, so don't forget to do that.
type GzipResponseWriter struct {
- gw *gzip.Writer
http.ResponseWriter
+ index int // Index for gzipWriterPools.
+ gw *gzip.Writer
}
// Write appends data to the gzip writer.
-func (w GzipResponseWriter) Write(b []byte) (int, error) {
- if _, ok := w.Header()["Content-Type"]; !ok {
+func (w *GzipResponseWriter) Write(b []byte) (int, error) {
+ // Lazily create the gzip.Writer, this allows empty bodies to be actually
+ // empty, for example in the case of status code 204 (no content).
+ if w.gw == nil {
+ w.init()
+ }
+
+ if _, ok := w.Header()[contentType]; !ok {
// If content type is not set, infer it from the uncompressed body.
- w.Header().Set("Content-Type", http.DetectContentType(b))
+ w.Header().Set(contentType, http.DetectContentType(b))
}
return w.gw.Write(b)
}
+// WriteHeader will check if the gzip writer needs to be lazily initiated and
+// then pass the code along to the underlying ResponseWriter.
+func (w *GzipResponseWriter) WriteHeader(code int) {
+ if w.gw == nil &&
+ code != http.StatusNotModified && code != http.StatusNoContent {
+ w.init()
+ }
+ w.ResponseWriter.WriteHeader(code)
+}
+
+// init graps a new gzip writer from the gzipWriterPool and writes the correct
+// content encoding header.
+func (w *GzipResponseWriter) init() {
+ // Bytes written during ServeHTTP are redirected to this gzip writer
+ // before being written to the underlying response.
+ gzw := gzipWriterPools[w.index].Get().(*gzip.Writer)
+ gzw.Reset(w.ResponseWriter)
+ w.gw = gzw
+ w.ResponseWriter.Header().Set(contentEncoding, "gzip")
+ // if the Content-Length is already set, then calls to Write on gzip
+ // will fail to set the Content-Length header since its already set
+ // See: https://github.com/golang/go/issues/14975
+ w.ResponseWriter.Header().Del(contentLength)
+}
+
+// Close will close the gzip.Writer and will put it back in the gzipWriterPool.
+func (w *GzipResponseWriter) Close() error {
+ if w.gw == nil {
+ return nil
+ }
+
+ err := w.gw.Close()
+ gzipWriterPools[w.index].Put(w.gw)
+ return err
+}
+
// Flush flushes the underlying *gzip.Writer and then the underlying
// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter
// an http.Flusher.
-func (w GzipResponseWriter) Flush() {
- w.gw.Flush()
+func (w *GzipResponseWriter) Flush() {
+ if w.gw != nil {
+ w.gw.Flush()
+ }
+
if fw, ok := w.ResponseWriter.(http.Flusher); ok {
fw.Flush()
}
}
+// MustNewGzipLevelHandler behaves just like NewGzipLevelHandler except that in
+// an error case it panics rather than returning an error.
+func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler {
+ wrap, err := NewGzipLevelHandler(level)
+ if err != nil {
+ panic(err)
+ }
+ return wrap
+}
+
+// NewGzipLevelHandler returns a wrapper function (often known as middleware)
+// which can be used to wrap an HTTP handler to transparently gzip the response
+// body if the client supports it (via the Accept-Encoding header). Responses will
+// be encoded at the given gzip compression level. An error will be returned only
+// if an invalid gzip compression level is given, so if one can ensure the level
+// is valid, the returned error can be safely ignored.
+func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) {
+ if level != gzip.DefaultCompression && (level < gzip.BestSpeed || level > gzip.BestCompression) {
+ return nil, fmt.Errorf("invalid compression level requested: %d", level)
+ }
+ return func(h http.Handler) http.Handler {
+ index := poolIndex(level)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add(vary, acceptEncoding)
+
+ if acceptsGzip(r) {
+ gw := &GzipResponseWriter{
+ ResponseWriter: w,
+ index: index,
+ }
+ defer gw.Close()
+
+ h.ServeHTTP(gw, r)
+ } else {
+ h.ServeHTTP(w, r)
+ }
+ })
+ }, nil
+}
+
// GzipHandler wraps an HTTP handler, to transparently gzip the response body if
-// the client supports it (via the Accept-Encoding header).
+// the client supports it (via the Accept-Encoding header). This will compress at
+// the default compression level.
func GzipHandler(h http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Add(vary, acceptEncoding)
-
- if acceptsGzip(r) {
- // Bytes written during ServeHTTP are redirected to this gzip writer
- // before being written to the underlying response.
- gzw := gzipWriterPool.Get().(*gzip.Writer)
- defer gzipWriterPool.Put(gzw)
- gzw.Reset(w)
- defer gzw.Close()
-
- w.Header().Set(contentEncoding, "gzip")
- h.ServeHTTP(GzipResponseWriter{gzw, w}, r)
- } else {
- h.ServeHTTP(w, r)
- }
- })
+ wrapper, _ := NewGzipLevelHandler(gzip.DefaultCompression)
+ return wrapper(h)
}
// acceptsGzip returns true if the given HTTP request indicates that it will
@@ -84,21 +189,20 @@ func acceptsGzip(r *http.Request) bool {
// parseEncodings attempts to parse a list of codings, per RFC 2616, as might
// appear in an Accept-Encoding header. It returns a map of content-codings to
-// quality values, and an error containing the errors encounted. It's probably
+// quality values, and an error containing the errors encountered. It's probably
// safe to ignore those, because silently ignoring errors is how the internet
// works.
//
-// See: http://tools.ietf.org/html/rfc2616#section-14.3
+// See: http://tools.ietf.org/html/rfc2616#section-14.3.
func parseEncodings(s string) (codings, error) {
c := make(codings)
- e := make([]string, 0)
+ var e []string
for _, ss := range strings.Split(s, ",") {
coding, qvalue, err := parseCoding(ss)
if err != nil {
e = append(e, err.Error())
-
} else {
c[coding] = qvalue
}
@@ -123,13 +227,11 @@ func parseCoding(s string) (coding string, qvalue float64, err error) {
if n == 0 {
coding = strings.ToLower(part)
-
} else if strings.HasPrefix(part, "q=") {
qvalue, err = strconv.ParseFloat(strings.TrimPrefix(part, "q="), 64)
if qvalue < 0.0 {
qvalue = 0.0
-
} else if qvalue > 1.0 {
qvalue = 1.0
}
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
index 9a62bcbaa..f2d44e04f 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip_test.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
@@ -3,10 +3,14 @@ package gziphandler
import (
"bytes"
"compress/gzip"
+ "fmt"
"io"
"io/ioutil"
+ "net"
"net/http"
"net/http/httptest"
+ "net/url"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
@@ -17,15 +21,15 @@ func TestParseEncodings(t *testing.T) {
examples := map[string]codings{
// Examples from RFC 2616
- "compress, gzip": codings{"compress": 1.0, "gzip": 1.0},
- "": codings{},
- "*": codings{"*": 1.0},
- "compress;q=0.5, gzip;q=1.0": codings{"compress": 0.5, "gzip": 1.0},
- "gzip;q=1.0, identity; q=0.5, *;q=0": codings{"gzip": 1.0, "identity": 0.5, "*": 0.0},
+ "compress, gzip": {"compress": 1.0, "gzip": 1.0},
+ "": {},
+ "*": {"*": 1.0},
+ "compress;q=0.5, gzip;q=1.0": {"compress": 0.5, "gzip": 1.0},
+ "gzip;q=1.0, identity; q=0.5, *;q=0": {"gzip": 1.0, "identity": 0.5, "*": 0.0},
// More random stuff
- "AAA;q=1": codings{"aaa": 1.0},
- "BBB ; q = 2": codings{"bbb": 1.0},
+ "AAA;q=1": {"aaa": 1.0},
+ "BBB ; q = 2": {"bbb": 1.0},
}
for eg, exp := range examples {
@@ -43,25 +47,27 @@ func TestGzipHandler(t *testing.T) {
// requests without accept-encoding are passed along as-is
req1, _ := http.NewRequest("GET", "/whatever", nil)
- res1 := httptest.NewRecorder()
- handler.ServeHTTP(res1, req1)
+ resp1 := httptest.NewRecorder()
+ handler.ServeHTTP(resp1, req1)
+ res1 := resp1.Result()
- assert.Equal(t, 200, res1.Code)
- assert.Equal(t, "", res1.Header().Get("Content-Encoding"))
- assert.Equal(t, "Accept-Encoding", res1.Header().Get("Vary"))
- assert.Equal(t, testBody, res1.Body.String())
+ assert.Equal(t, 200, res1.StatusCode)
+ assert.Equal(t, "", res1.Header.Get("Content-Encoding"))
+ assert.Equal(t, "Accept-Encoding", res1.Header.Get("Vary"))
+ assert.Equal(t, testBody, resp1.Body.String())
// but requests with accept-encoding:gzip are compressed if possible
req2, _ := http.NewRequest("GET", "/whatever", nil)
req2.Header.Set("Accept-Encoding", "gzip")
- res2 := httptest.NewRecorder()
- handler.ServeHTTP(res2, req2)
+ resp2 := httptest.NewRecorder()
+ handler.ServeHTTP(resp2, req2)
+ res2 := resp2.Result()
- assert.Equal(t, 200, res2.Code)
- assert.Equal(t, "gzip", res2.Header().Get("Content-Encoding"))
- assert.Equal(t, "Accept-Encoding", res2.Header().Get("Vary"))
- assert.Equal(t, gzipStr(testBody), res2.Body.Bytes())
+ assert.Equal(t, 200, res2.StatusCode)
+ assert.Equal(t, "gzip", res2.Header.Get("Content-Encoding"))
+ assert.Equal(t, "Accept-Encoding", res2.Header.Get("Vary"))
+ assert.Equal(t, gzipStrLevel(testBody, gzip.DefaultCompression), resp2.Body.Bytes())
// content-type header is correctly set based on uncompressed body
@@ -73,6 +79,142 @@ func TestGzipHandler(t *testing.T) {
assert.Equal(t, http.DetectContentType([]byte(testBody)), res3.Header().Get("Content-Type"))
}
+func TestNewGzipLevelHandler(t *testing.T) {
+ testBody := "aaabbbccc"
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ io.WriteString(w, testBody)
+ })
+
+ for lvl := gzip.BestSpeed; lvl <= gzip.BestCompression; lvl++ {
+ wrapper, err := NewGzipLevelHandler(lvl)
+ if !assert.Nil(t, err, "NewGzipLevleHandler returned error for level:", lvl) {
+ continue
+ }
+
+ req, _ := http.NewRequest("GET", "/whatever", nil)
+ req.Header.Set("Accept-Encoding", "gzip")
+ resp := httptest.NewRecorder()
+ wrapper(handler).ServeHTTP(resp, req)
+ res := resp.Result()
+
+ assert.Equal(t, 200, res.StatusCode)
+ assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
+ assert.Equal(t, "Accept-Encoding", res.Header.Get("Vary"))
+ assert.Equal(t, gzipStrLevel(testBody, lvl), resp.Body.Bytes())
+
+ }
+}
+
+func TestNewGzipLevelHandlerReturnsErrorForInvalidLevels(t *testing.T) {
+ var err error
+ _, err = NewGzipLevelHandler(-42)
+ assert.NotNil(t, err)
+
+ _, err = NewGzipLevelHandler(42)
+ assert.NotNil(t, err)
+}
+
+func TestMustNewGzipLevelHandlerWillPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Error("panic was not called")
+ }
+ }()
+
+ _ = MustNewGzipLevelHandler(-42)
+}
+
+func TestGzipHandlerNoBody(t *testing.T) {
+ tests := []struct {
+ statusCode int
+ contentEncoding string
+ bodyLen int
+ }{
+ // Body must be empty.
+ {http.StatusNoContent, "", 0},
+ {http.StatusNotModified, "", 0},
+ // Body is going to get gzip'd no matter what.
+ {http.StatusOK, "gzip", 23},
+ }
+
+ for num, test := range tests {
+ handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(test.statusCode)
+ }))
+
+ rec := httptest.NewRecorder()
+ // TODO: in Go1.7 httptest.NewRequest was introduced this should be used
+ // once 1.6 is not longer supported.
+ req := &http.Request{
+ Method: "GET",
+ URL: &url.URL{Path: "/"},
+ Proto: "HTTP/1.1",
+ ProtoMinor: 1,
+ RemoteAddr: "192.0.2.1:1234",
+ Header: make(http.Header),
+ }
+ req.Header.Set("Accept-Encoding", "gzip")
+ handler.ServeHTTP(rec, req)
+
+ body, err := ioutil.ReadAll(rec.Body)
+ if err != nil {
+ t.Fatalf("Unexpected error reading response body: %v", err)
+ }
+
+ header := rec.Header()
+ assert.Equal(t, test.contentEncoding, header.Get("Content-Encoding"), fmt.Sprintf("for test iteration %d", num))
+ assert.Equal(t, "Accept-Encoding", header.Get("Vary"), fmt.Sprintf("for test iteration %d", num))
+ assert.Equal(t, test.bodyLen, len(body), fmt.Sprintf("for test iteration %d", num))
+ }
+}
+
+func TestGzipHandlerContentLength(t *testing.T) {
+ b := []byte("testtesttesttesttesttesttesttesttesttesttesttesttest")
+ handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Length", strconv.Itoa(len(b)))
+ w.Write(b)
+ }))
+ // httptest.NewRecorder doesn't give you access to the Content-Length
+ // header so instead, we create a server on a random port and make
+ // a request to that instead
+ ln, err := net.Listen("tcp", "127.0.0.1:")
+ if err != nil {
+ t.Fatalf("failed creating listen socket: %v", err)
+ }
+ defer ln.Close()
+ srv := &http.Server{
+ Handler: handler,
+ }
+ go srv.Serve(ln)
+
+ req := &http.Request{
+ Method: "GET",
+ URL: &url.URL{Path: "/", Scheme: "http", Host: ln.Addr().String()},
+ Header: make(http.Header),
+ Close: true,
+ }
+ req.Header.Set("Accept-Encoding", "gzip")
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatalf("Unexpected error making http request: %v", err)
+ }
+ defer res.Body.Close()
+
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("Unexpected error reading response body: %v", err)
+ }
+
+ l, err := strconv.Atoi(res.Header.Get("Content-Length"))
+ if err != nil {
+ t.Fatalf("Unexpected error parsing Content-Length: %v", err)
+ }
+ assert.Len(t, body, l)
+ assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
+ assert.NotEqual(t, b, body)
+}
+
// --------------------------------------------------------------------
func BenchmarkGzipHandler_S2k(b *testing.B) { benchmark(b, false, 2048) }
@@ -84,9 +226,9 @@ func BenchmarkGzipHandler_P100k(b *testing.B) { benchmark(b, true, 102400) }
// --------------------------------------------------------------------
-func gzipStr(s string) []byte {
+func gzipStrLevel(s string, lvl int) []byte {
var b bytes.Buffer
- w := gzip.NewWriter(&b)
+ w, _ := gzip.NewWriterLevel(&b, lvl)
io.WriteString(w, s)
w.Close()
return b.Bytes()