Browse Source

initial project.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Bo-Yi Wu 9 years ago
commit
a5fd55f06f
4 changed files with 178 additions and 0 deletions
  1. 10 0
      Godeps/Godeps.json
  2. 19 0
      example/example.go
  3. 68 0
      gzip.go
  4. 81 0
      gzip_test.go

+ 10 - 0
Godeps/Godeps.json

@@ -0,0 +1,10 @@
+{
+	"ImportPath": "github.com/gin-gonic/contrib/gzip",
+	"GoVersion": "go1.3",
+	"Deps": [
+		{
+			"ImportPath": "github.com/gin-gonic/gin",
+			"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
+		}
+	]
+}

+ 19 - 0
example/example.go

@@ -0,0 +1,19 @@
+package main
+
+import (
+	"fmt"
+	"github.com/gin-gonic/contrib/gzip"
+	"github.com/gin-gonic/gin"
+	"time"
+)
+
+func main() {
+	r := gin.Default()
+	r.Use(gzip.Gzip(gzip.DefaultCompression))
+	r.GET("/ping", func(c *gin.Context) {
+		c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
+	})
+
+	// Listen and Server in 0.0.0.0:8080
+	r.Run(":8080")
+}

+ 68 - 0
gzip.go

@@ -0,0 +1,68 @@
+package gzip
+
+import (
+	"compress/gzip"
+	"net/http"
+	"path/filepath"
+	"strings"
+
+	"github.com/gin-gonic/gin"
+)
+
+const (
+	BestCompression    = gzip.BestCompression
+	BestSpeed          = gzip.BestSpeed
+	DefaultCompression = gzip.DefaultCompression
+	NoCompression      = gzip.NoCompression
+)
+
+func Gzip(level int) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		if !shouldCompress(c.Request) {
+			return
+		}
+		gz, err := gzip.NewWriterLevel(c.Writer, level)
+		if err != nil {
+			return
+		}
+
+		c.Header("Content-Encoding", "gzip")
+		c.Header("Vary", "Accept-Encoding")
+		c.Writer = &gzipWriter{c.Writer, gz}
+		defer func() {
+			c.Header("Content-Length", "0")
+			gz.Close()
+		}()
+		c.Next()
+	}
+}
+
+type gzipWriter struct {
+	gin.ResponseWriter
+	writer *gzip.Writer
+}
+
+func (g *gzipWriter) WriteString(s string) (int, error) {
+	return g.writer.Write([]byte(s))
+}
+
+func (g *gzipWriter) Write(data []byte) (int, error) {
+	return g.writer.Write(data)
+}
+
+func shouldCompress(req *http.Request) bool {
+	if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
+		return false
+	}
+	extension := filepath.Ext(req.URL.Path)
+	if len(extension) < 4 { // fast path
+		return true
+	}
+
+	switch extension {
+	case ".png", ".gif", ".jpeg", ".jpg":
+		return false
+	default:
+		return true
+	}
+}

+ 81 - 0
gzip_test.go

@@ -0,0 +1,81 @@
+package gzip
+
+import (
+	"compress/gzip"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"strconv"
+	"testing"
+
+	"github.com/gin-gonic/gin"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	testResponse = "Gzip Test Response "
+)
+
+func newServer() *gin.Engine {
+	router := gin.Default()
+	router.Use(Gzip(DefaultCompression))
+	router.GET("/", func(c *gin.Context) {
+		c.Header("Content-Length", strconv.Itoa(len(testResponse)))
+		c.String(200, testResponse)
+	})
+	return router
+}
+
+func TestGzip(t *testing.T) {
+	req, _ := http.NewRequest("GET", "/", nil)
+	req.Header.Add("Accept-Encoding", "gzip")
+
+	w := httptest.NewRecorder()
+	r := newServer()
+	r.ServeHTTP(w, req)
+
+	assert.Equal(t, w.Code, 200)
+	assert.Equal(t, w.Header().Get("Content-Encoding"), "gzip")
+	assert.Equal(t, w.Header().Get("Vary"), "Accept-Encoding")
+	assert.Equal(t, w.Header().Get("Content-Length"), "0")
+	assert.NotEqual(t, w.Body.Len(), 19)
+
+	gr, err := gzip.NewReader(w.Body)
+	assert.NoError(t, err)
+	defer gr.Close()
+
+	body, _ := ioutil.ReadAll(gr)
+	assert.Equal(t, string(body), testResponse)
+}
+
+func TestGzipPNG(t *testing.T) {
+	req, _ := http.NewRequest("GET", "/image.png", nil)
+	req.Header.Add("Accept-Encoding", "gzip")
+
+	router := gin.New()
+	router.Use(Gzip(DefaultCompression))
+	router.GET("/image.png", func(c *gin.Context) {
+		c.String(200, "this is a PNG!")
+	})
+
+	w := httptest.NewRecorder()
+	router.ServeHTTP(w, req)
+
+	assert.Equal(t, w.Code, 200)
+	assert.Equal(t, w.Header().Get("Content-Encoding"), "")
+	assert.Equal(t, w.Header().Get("Vary"), "")
+	assert.Equal(t, w.Body.String(), "this is a PNG!")
+}
+
+func TestNoGzip(t *testing.T) {
+	req, _ := http.NewRequest("GET", "/", nil)
+
+	w := httptest.NewRecorder()
+	r := newServer()
+	r.ServeHTTP(w, req)
+
+	assert.Equal(t, w.Code, 200)
+	assert.Equal(t, w.Header().Get("Content-Encoding"), "")
+	assert.Equal(t, w.Header().Get("Content-Length"), "19")
+	assert.Equal(t, w.Body.String(), testResponse)
+}