New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reverse proxy with gzip generates unexpected EOFs when reading body. #14975
Comments
Note that I can also reproduce the response failures with curl, but only when asking for a compressed response:
|
You didn't check errors from
The content length field is set here: server.go |
@opennota Great catch, thanks! I fixed this with:
Do you think there's a better fix? |
@optimality, that seems good. |
Does it work? I don't think WriteHeader(code int) is actually called in this case although it should be. |
Yeah, this definitely fixed it, and WriteHeader is definitely called. Check out the following example 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 (w gzipResponseWriter) WriteHeader(code int) {
fmt.Printf("Writing header: %v\n", code)
w.Header().Del("Content-Length")
w.ResponseWriter.WriteHeader(code)
}
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 func() {
err := gz.Close()
if err != nil {
fmt.Printf("Error closing gzip: %+v\n", err)
}
}()
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))
} |
I'm trying to gzip the response from an API server that uses reverse proxies to send requests to a number of other servers. However, whenever I try to gzip the results, reading the body generates io.ErrUnexpectedEOF. I suspect there's a bug in how ReverseProxy handles the gzip writer.
go version
)?go version go1.6 darwin/amd64
go env
)?darwin amd64 (OS X 10.11.3)
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
Demo code:
No errors.
Error when reading proxied response.
The text was updated successfully, but these errors were encountered: