From 77a055fec377fd0767541c20e792497c2014310c Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Mon, 4 Apr 2022 16:49:01 +0000 Subject: [PATCH] Handle compressed response and follow redirects in openvsx proxy --- .../openvsx-proxy/pkg/modifyresponse.go | 40 ++++++++++++++++--- .../openvsx-proxy/pkg/openvsxproxy_test.go | 33 +++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/components/openvsx-proxy/pkg/modifyresponse.go b/components/openvsx-proxy/pkg/modifyresponse.go index bb6710acacf134..d663d7bb279581 100644 --- a/components/openvsx-proxy/pkg/modifyresponse.go +++ b/components/openvsx-proxy/pkg/modifyresponse.go @@ -6,6 +6,7 @@ package pkg import ( "bytes" + "compress/gzip" "fmt" "io/ioutil" "net/http" @@ -48,19 +49,19 @@ func (o *OpenVSXProxy) ModifyResponse(r *http.Response) error { return nil } - body, err := ioutil.ReadAll(r.Body) + rawBody, err := ioutil.ReadAll(r.Body) if err != nil { - log.WithFields(logFields).WithError(err).Error("error reading response body") + log.WithFields(logFields).WithError(err).Error("error reading response raw body") return err } r.Body.Close() - r.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + r.Body = ioutil.NopCloser(bytes.NewBuffer(rawBody)) if r.StatusCode >= 500 || r.StatusCode == http.StatusTooManyRequests || r.StatusCode == http.StatusRequestTimeout { // use cache if exists bodyLogField := "(binary)" - if utf8.Valid(body) { - bodyStr := string(body) + if utf8.Valid(rawBody) { + bodyStr := string(rawBody) truncatedSuffix := "" if len(bodyStr) > 500 { truncatedSuffix = "... [truncated]" @@ -93,13 +94,42 @@ func (o *OpenVSXProxy) ModifyResponse(r *http.Response) error { } // no error (status code < 500) + body := rawBody contentType := r.Header.Get("Content-Type") if strings.HasPrefix(contentType, "application/json") { + isCompressedResponse := strings.EqualFold(r.Header.Get("Content-Encoding"), "gzip") + if isCompressedResponse { + gzipReader, err := gzip.NewReader(ioutil.NopCloser(bytes.NewBuffer(rawBody))) + if err != nil { + log.WithFields(logFields).WithError(err) + return nil + } + + body, err = ioutil.ReadAll(gzipReader) + if err != nil { + log.WithFields(logFields).WithError(err).Error("error reading compressed response body") + return nil + } + gzipReader.Close() + } + if log.Log.Level >= logrus.DebugLevel { log.WithFields(logFields).Debugf("replacing %d occurence(s) of '%s' in response body ...", strings.Count(string(body), o.Config.URLUpstream), o.Config.URLUpstream) } bodyStr := strings.ReplaceAll(string(body), o.Config.URLUpstream, o.Config.URLLocal) body = []byte(bodyStr) + + if isCompressedResponse { + var b bytes.Buffer + gzipWriter := gzip.NewWriter(&b) + _, err = gzipWriter.Write(body) + if err != nil { + log.WithFields(logFields).WithError(err).Error("error writing compressed response body") + return nil + } + gzipWriter.Close() + body = b.Bytes() + } } else { log.WithFields(logFields).Debugf("response is not JSON but '%s', skipping replacing '%s' in response body", contentType, o.Config.URLUpstream) } diff --git a/components/openvsx-proxy/pkg/openvsxproxy_test.go b/components/openvsx-proxy/pkg/openvsxproxy_test.go index cff8d307452b26..3c16a34eac3d13 100644 --- a/components/openvsx-proxy/pkg/openvsxproxy_test.go +++ b/components/openvsx-proxy/pkg/openvsxproxy_test.go @@ -6,6 +6,7 @@ package pkg import ( "bytes" + "compress/gzip" "fmt" "io" "net/http" @@ -55,6 +56,38 @@ func TestReplaceHostInJSONResponse(t *testing.T) { } } +func TestReplaceHostInCompressedJSONResponse(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + bodyBytes, _ := io.ReadAll(r.Body) + rw.Header().Set("Content-Type", "application/json") + rw.Header().Set("Content-Encoding", "gzip") + + var b bytes.Buffer + w := gzip.NewWriter(&b) + w.Write([]byte(fmt.Sprintf("Hello %s!", string(bodyBytes)))) + w.Close() + rw.Write(b.Bytes()) + })) + defer backend.Close() + + frontend, _ := createFrontend(backend.URL) + defer frontend.Close() + + frontendClient := frontend.Client() + + requestBody := backend.URL + req, _ := http.NewRequest("POST", frontend.URL, bytes.NewBuffer([]byte(requestBody))) + req.Close = true + res, err := frontendClient.Do(req) + if err != nil { + t.Fatal(err) + } + expectedResponse := fmt.Sprintf("Hello %s!", frontend.URL) + if bodyBytes, _ := io.ReadAll(res.Body); string(bodyBytes) != expectedResponse { + t.Errorf("got body '%s'; expected '%s'", string(bodyBytes), expectedResponse) + } +} + func TestNotReplaceHostInNonJSONResponse(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { bodyBytes, _ := io.ReadAll(r.Body)