From 5c318f9037cbb3f11f12485fe56fc15a19a5e07d Mon Sep 17 00:00:00 2001 From: Andy Balholm Date: Tue, 30 Apr 2019 14:53:06 -0700 Subject: [PATCH] Add HTTPCompressor for HTTP Content-Encoding negotiation. --- go.mod | 5 +++++ go.sum | 2 ++ writer.go | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cb505fa --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/andybalholm/brotli + +go 1.12 + +require github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2ed61f3 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw= +github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= diff --git a/writer.go b/writer.go index 96541b6..92c128c 100644 --- a/writer.go +++ b/writer.go @@ -1,8 +1,12 @@ package brotli import ( + "compress/gzip" "errors" "io" + "net/http" + + "github.com/golang/gddo/httputil" ) const ( @@ -114,3 +118,38 @@ func (w *Writer) Close() error { func (w *Writer) Write(p []byte) (n int, err error) { return w.writeChunk(p, operationProcess) } + +type nopCloser struct { + io.Writer +} + +func (nopCloser) Close() error { return nil } + +// HTTPCompressor chooses a compression method (brotli, gzip, or none) based on +// the Accept-Encoding header, sets the Content-Encoding header, and returns a +// WriteCloser that implements that compression. The Close method must be called +// before the current HTTP handler returns. +// +// Due to https://github.com/golang/go/issues/31753, the response will not be +// compressed unless you set a Content-Type header before you call +// HTTPCompressor. +func HTTPCompressor(w http.ResponseWriter, r *http.Request) io.WriteCloser { + if w.Header().Get("Content-Type") == "" { + return nopCloser{w} + } + + if w.Header().Get("Vary") == "" { + w.Header().Set("Vary", "Accept-Encoding") + } + + encoding := httputil.NegotiateContentEncoding(r, []string{"br", "gzip"}) + switch encoding { + case "br": + w.Header().Set("Content-Encoding", "br") + return NewWriter(w) + case "gzip": + w.Header().Set("Content-Encoding", "gzip") + return gzip.NewWriter(w) + } + return nopCloser{w} +}