From 0ffe9de1e32ac0f30e1fa14d9e3a0ec6d721682b Mon Sep 17 00:00:00 2001 From: Lee Brown Date: Sun, 19 Jan 2020 22:55:29 -0900 Subject: [PATCH] Optional minify response --- build/cicd/internal/config/service.go | 2 + cmd/web-api/main.go | 6 ++ cmd/web-api/sample.env | 1 + cmd/web-app/main.go | 6 ++ cmd/web-app/sample.env | 3 +- go.mod | 2 + go.sum | 2 + internal/mid/minify.go | 55 +++++++++++++++++++ .../platform/web/tmplrender/tmplrender.go | 21 +++++++ 9 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 internal/mid/minify.go diff --git a/build/cicd/internal/config/service.go b/build/cicd/internal/config/service.go index 43f2d85..a10e0d3 100644 --- a/build/cicd/internal/config/service.go +++ b/build/cicd/internal/config/service.go @@ -610,6 +610,7 @@ func NewService(log *log.Logger, serviceName string, cfg *devdeploy.Config) (*de ecsKeyValuePair("WEB_APP_DB_DISABLE_TLS", strconv.FormatBool(vars.DbDisableTLS)), ecsKeyValuePair("WEB_APP_AWS_S3_BUCKET_PRIVATE", vars.AwsS3BucketNamePrivate), ecsKeyValuePair("WEB_APP_AWS_S3_BUCKET_PUBLIC", vars.AwsS3BucketNamePublic), + ecsKeyValuePair("WEB_APP_SERVICE_MINIFY", "true"), ) // Enable image resize s3 is enabled. @@ -727,6 +728,7 @@ func NewService(log *log.Logger, serviceName string, cfg *devdeploy.Config) (*de ecsKeyValuePair("WEB_API_DB_DISABLE_TLS", strconv.FormatBool(vars.DbDisableTLS)), ecsKeyValuePair("WEB_API_AWS_S3_BUCKET_PRIVATE", vars.AwsS3BucketNamePrivate), ecsKeyValuePair("WEB_API_AWS_S3_BUCKET_PUBLIC", vars.AwsS3BucketNamePublic), + ecsKeyValuePair("WEB_API_SERVICE_MINIFY", "true"), ) // When no Elastic Load Balance is used, tasks need to be able to directly update the Route 53 records. diff --git a/cmd/web-api/main.go b/cmd/web-api/main.go index b9c9ede..33381e6 100644 --- a/cmd/web-api/main.go +++ b/cmd/web-api/main.go @@ -102,6 +102,7 @@ func main() { HostNames []string `envconfig:"HOST_NAMES" example:"alternative-subdomain.example.saasstartupkit.com"` EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"` TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"` + Minify bool `envconfig:"MINIFY"` DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"` ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"` ScaleToZero time.Duration `envconfig:"SCALE_TO_ZERO"` @@ -491,6 +492,11 @@ func main() { // Add the translator middleware for localization. appCtx.PostAppMiddleware = append(appCtx.PostAppMiddleware, mid.Translator(webcontext.UniversalTranslator())) + // Apply response minification if enabled. + if cfg.Service.Minify { + appCtx.PostAppMiddleware = append(appCtx.PostAppMiddleware, mid.Minify()) + } + // ========================================================================= // Start Tracing Support th := fmt.Sprintf("%s:%d", cfg.Trace.Host, cfg.Trace.Port) diff --git a/cmd/web-api/sample.env b/cmd/web-api/sample.env index b7e0571..5ee9317 100644 --- a/cmd/web-api/sample.env +++ b/cmd/web-api/sample.env @@ -3,3 +3,4 @@ export WEB_API_DB_USER=postgres export WEB_API_DB_PASS=postgres export WEB_API_DB_DISABLE_TLS=true export WEB_API_SERVICE_EMAIL_SENDER=valdez@example.com +export WEB_API_SERVICE_MINIFY=false diff --git a/cmd/web-app/main.go b/cmd/web-app/main.go index 219ab63..ffd9105 100644 --- a/cmd/web-app/main.go +++ b/cmd/web-app/main.go @@ -104,6 +104,7 @@ func main() { CloudFrontEnabled bool `envconfig:"CLOUDFRONT_ENABLED"` ImgResizeEnabled bool `envconfig:"IMG_RESIZE_ENABLED"` } + Minify bool `envconfig:"MINIFY"` SessionName string `default:"" envconfig:"SESSION_NAME"` DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"` ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"` @@ -501,6 +502,11 @@ func main() { // Add the translator middleware for localization. appCtx.PostAppMiddleware = append(appCtx.PostAppMiddleware, mid.Translator(webcontext.UniversalTranslator())) + // Apply response minification if enabled. + if cfg.Service.Minify { + appCtx.PostAppMiddleware = append(appCtx.PostAppMiddleware, mid.Minify()) + } + // Generate the new session store and append it to the global list of middlewares. // Init session store diff --git a/cmd/web-app/sample.env b/cmd/web-app/sample.env index cb9bedc..3c55ae0 100644 --- a/cmd/web-app/sample.env +++ b/cmd/web-app/sample.env @@ -2,4 +2,5 @@ export WEB_APP_DB_HOST=127.0.0.1:5433 export WEB_APP_DB_USER=postgres export WEB_APP_DB_PASS=postgres export WEB_APP_DB_DISABLE_TLS=true -export WEB_APP_SERVICE_EMAIL_SENDER=valdez@example.com \ No newline at end of file +export WEB_APP_SERVICE_EMAIL_SENDER=valdez@example.com +export WEB_APP_SERVICE_MINIFY=false diff --git a/go.mod b/go.mod index a9edbd0..dd1c9e9 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,8 @@ require ( github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 github.com/stretchr/testify v1.4.0 github.com/sudo-suhas/symcrypto v1.0.0 + github.com/tdewolff/minify v2.3.6+incompatible + github.com/tdewolff/parse v2.3.4+incompatible // indirect github.com/tinylib/msgp v1.1.0 // indirect github.com/urfave/cli v1.22.2 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 diff --git a/go.sum b/go.sum index bd4d462..4b3ced1 100644 --- a/go.sum +++ b/go.sum @@ -221,6 +221,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/sudo-suhas/symcrypto v1.0.0 h1:VG6FdACf5XeXFQUzeA++aB6snNThz0OFlmUHiCddi2s= github.com/sudo-suhas/symcrypto v1.0.0/go.mod h1:g/faGDjhlF/DXdqp3+SQ0LmhPcv4iYaIRjcm/Q60+68= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= diff --git a/internal/mid/minify.go b/internal/mid/minify.go new file mode 100644 index 0000000..994ef58 --- /dev/null +++ b/internal/mid/minify.go @@ -0,0 +1,55 @@ +package mid + +import ( + "context" + "net/http" + "regexp" + + "geeks-accelerator/oss/saas-starter-kit/internal/platform/web" + "github.com/tdewolff/minify" + "github.com/tdewolff/minify/css" + "github.com/tdewolff/minify/html" + "github.com/tdewolff/minify/js" + "github.com/tdewolff/minify/json" + "github.com/tdewolff/minify/svg" + "github.com/tdewolff/minify/xml" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" +) + +// Minify provides minification +func Minify() web.Middleware { + + m := minify.New() + m.AddFunc("text/css", css.Minify) + m.AddFunc("text/html", html.Minify) + m.AddFunc("image/svg+xml", svg.Minify) + m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify) + m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify) + m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify) + + m.AddFunc(web.MIMEApplicationJSON, json.Minify) + m.AddFunc(web.MIMEApplicationJSONCharsetUTF8, json.Minify) + m.AddFunc(web.MIMETextHTML, html.Minify) + m.AddFunc(web.MIMETextHTMLCharsetUTF8, html.Minify) + m.AddFunc(web.MIMETextPlain, html.Minify) + m.AddFunc(web.MIMETextPlainCharsetUTF8, html.Minify) + + // This is the actual middleware function to be executed. + f := func(after web.Handler) web.Handler { + + // Wrap this handler around the next one provided. + h := func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error { + span, ctx := tracer.StartSpanFromContext(ctx, "internal.mid.minify") + defer span.Finish() + + mw := m.ResponseWriter(w, r) + defer mw.Close() + + return after(ctx, mw, r, params) + } + + return h + } + + return f +} diff --git a/internal/platform/web/tmplrender/tmplrender.go b/internal/platform/web/tmplrender/tmplrender.go index d461064..7a26099 100644 --- a/internal/platform/web/tmplrender/tmplrender.go +++ b/internal/platform/web/tmplrender/tmplrender.go @@ -288,6 +288,27 @@ func (r *TemplateRenderer) Render(ctx context.Context, w http.ResponseWriter, re return nil } + // Set the status code for the request logger middleware. + // If the context is missing this value, request the service + // to be shutdown gracefully. + v, err := webcontext.ContextValues(ctx) + if err != nil { + return err + } + v.StatusCode = statusCode + + // If there is nothing to marshal then set status code and return. + if statusCode == http.StatusNoContent { + w.WriteHeader(statusCode) + return nil + } + + // Set the content type and headers once we know marshaling has succeeded. + w.Header().Set("Content-Type", contentType) + + // Write the status code to the response. + w.WriteHeader(statusCode) + // If the template has not been rendered yet or hot reload is enabled, // then parse the template files. t, ok := r.templates[templateContentName]