Skip to content

Instantly share code, notes, and snippets.

@goors
Last active May 6, 2017 16:12
Show Gist options
  • Save goors/23dd0115a6fe2605d115937e7e4f06de to your computer and use it in GitHub Desktop.
Save goors/23dd0115a6fe2605d115937e7e4f06de to your computer and use it in GitHub Desktop.
Using https://github.com/tdewolff/minify to minify html response in golang Revel.
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Jeevanandam M. (jeeva@myjeeva.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Article: http://myjeeva.com/minify-filter-revel-framework-go-lang.html
package filter
import (
"io"
"net/http"
"regexp"
"github.com/revel/revel"
"github.com/tdewolff/minify"
"github.com/tdewolff/minify/html"
"github.com/tdewolff/minify/css"
"github.com/tdewolff/minify/js"
)
var (
htmlMimeType *regexp.Regexp
m *minify.M
)
type MinifyWriter struct {
http.ResponseWriter
minifyWriter io.WriteCloser
shouldMinify bool
headersWritten bool
closeNotify chan bool
parentNotify <-chan bool
closed bool
}
func init() {
m = minify.New()
m.AddFunc("text/css", css.Minify)
m.AddFunc("text/javascript", js.Minify)
m.Add("text/html", &html.Minifier{
KeepDefaultAttrVals: true,
KeepWhitespace: false,
})
htmlMimeType = regexp.MustCompile("[text|application]/html")
}
func Minify(c *revel.Controller, fc []revel.Filter) {
fc[0](c, fc[1:])
if revel.Config.BoolDefault("results.minify", false) {
if c.Response.Status != http.StatusNoContent && c.Response.Status != http.StatusNotModified {
// add with options
mw := m.Writer("text/html; charset=utf-8", c.Response.Out)
writer := MinifyWriter{c.Response.Out, mw, false, false, make(chan bool, 1), nil, false}
w, ok := c.Response.Out.(http.CloseNotifier)
if ok {
writer.parentNotify = w.CloseNotify()
}
c.Response.Out = &writer
} else {
revel.TRACE.Printf("Minify disabled for response status (%d)", c.Response.Status)
}
}
}
func (c MinifyWriter) CloseNotify() <-chan bool {
if c.parentNotify != nil {
return c.parentNotify
}
return c.closeNotify
}
func (c *MinifyWriter) WriteHeader(status int) {
c.headersWritten = true
c.prepareHeader()
c.ResponseWriter.WriteHeader(status)
}
func (c *MinifyWriter) prepareHeader() {
// minify is only applied to HTML content
if htmlMimeType.MatchString(c.Header().Get("Content-Type")) {
c.shouldMinify = true
c.Header().Del("Content-Length")
}
}
func (c *MinifyWriter) Close() error {
if c.shouldMinify {
c.minifyWriter.Close()
}
if w, ok := c.ResponseWriter.(io.Closer); ok {
w.Close()
}
// Non-blocking write to the closenotifier, if we for some reason should
// get called multiple times
select {
case c.closeNotify <- true:
default:
}
c.closed = true
return nil
}
func (c *MinifyWriter) Write(b []byte) (int, error) {
// Abort if parent has been closed
if c.parentNotify != nil {
select {
case <-c.parentNotify:
return 0, io.ErrClosedPipe
default:
}
}
// Abort if we ourselves have been closed
if c.closed {
return 0, io.ErrClosedPipe
}
if !c.headersWritten {
c.prepareHeader()
c.headersWritten = true
}
if c.shouldMinify {
return c.minifyWriter.Write(b)
} else {
return c.ResponseWriter.Write(b)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment