I’m trying to write a multiplexer in front of a variety of services using reverse proxies. However, when I try to gzip the response from the reverse proxy, the resulting response is bad. I get “unexpected EOF” when using ioutil.ReadAll
, and curl
complains about curl: (18) transfer closed with 3 bytes remaining to read
.
I suspect this is a bug in ReverseProxy, and filed https://github.com/golang/go/issues/14975 to that end. However, I thought I’d also check here in case anybody else has done something like this and gotten it to work.
Sample code:
package main
import (
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
)
// Gzip from https://gist.github.com/the42/1956518
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func makeGzipHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
fn(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w}
fn(gzr, r)
}
}
// Handler that does not set a content length, so, golang uses chunking.
func handler(w http.ResponseWriter, r *http.Request) {
message := "Hello, world!"
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(message))
}
// Constructs a reverse proxy to the given port.
func reverseProxy(port string) func(http.ResponseWriter, *http.Request) {
url, err := url.Parse("http://127.0.0.1" + port)
if err != nil {
panic(err)
}
return httputil.NewSingleHostReverseProxy(url).ServeHTTP
}
// Gets the content from the given server, then returns the error from reading the body.
func get(server http.Server) error {
resp, err := http.Get("http://127.0.0.1" + server.Addr)
if err != nil {
panic(err)
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
return err
}
func main() {
server := http.Server{
Addr: ":2000",
Handler: http.HandlerFunc(handler),
}
go server.ListenAndServe()
proxyServer := http.Server{
Addr: ":4000",
Handler: makeGzipHandler(reverseProxy(server.Addr)),
}
go proxyServer.ListenAndServe()
time.Sleep(10 * time.Millisecond)
fmt.Printf("Server err: %v\n", get(server))
fmt.Printf("Proxy server err: %v\n", get(proxyServer))
}
Anybody seen anything like this before, or gotten it to work?