Skip to content

Commit

Permalink
Handle compressed response and follow redirects in openvsx proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanp413 authored and Simon Emms committed Apr 11, 2022
1 parent 9e750f3 commit 77a055f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 5 deletions.
40 changes: 35 additions & 5 deletions components/openvsx-proxy/pkg/modifyresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package pkg

import (
"bytes"
"compress/gzip"
"fmt"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -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]"
Expand Down Expand Up @@ -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)
}
Expand Down
33 changes: 33 additions & 0 deletions components/openvsx-proxy/pkg/openvsxproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package pkg

import (
"bytes"
"compress/gzip"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 77a055f

Please sign in to comment.