From 2baa69763856b71da702260b9164d404b8305c98 Mon Sep 17 00:00:00 2001 From: Chris Pine Date: Fri, 2 Oct 2020 15:34:20 -0700 Subject: [PATCH 1/5] gzip http requests --- internal/api/api.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 00b3fafed9..1ae2c96956 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -13,10 +13,11 @@ import ( "strings" "github.com/hashicorp/go-multierror" - "github.com/jig/teereadcloser" + ioaux "github.com/jig/teereadcloser" "github.com/kballard/go-shellquote" "github.com/mattn/go-isatty" "github.com/pkg/errors" + "github.com/sourcegraph/codeintelutils" ) // Client instances provide methods to create API requests. @@ -126,6 +127,7 @@ func (c *client) NewHTTPRequest(ctx context.Context, method, p string, body io.R for k, v := range c.opts.AdditionalHeaders { req.Header.Set(k, v) } + req.Header.Set("Content-Encoding", "gzip") return req, nil } @@ -164,7 +166,8 @@ func (r *request) do(ctx context.Context, result interface{}) (bool, error) { } // Create the HTTP request. - req, err := r.client.NewHTTPRequest(ctx, "POST", ".api/graphql", bytes.NewBuffer(reqBody)) + zipped := codeintelutils.Gzip(bytes.NewBuffer(reqBody)) + req, err := r.client.NewHTTPRequest(ctx, "POST", ".api/graphql", zipped) if err != nil { return false, err } From eb9996b65a8be5edf381827a6cef12fd0a5e8544 Mon Sep 17 00:00:00 2001 From: Chris Pine Date: Mon, 5 Oct 2020 08:54:32 -0700 Subject: [PATCH 2/5] remove unused alias --- internal/api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/api.go b/internal/api/api.go index 1ae2c96956..167ed2b3c4 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -13,7 +13,7 @@ import ( "strings" "github.com/hashicorp/go-multierror" - ioaux "github.com/jig/teereadcloser" + "github.com/jig/teereadcloser" "github.com/kballard/go-shellquote" "github.com/mattn/go-isatty" "github.com/pkg/errors" From deb10206ca2b8d21a2534e71c2c65d6ebfde64f3 Mon Sep 17 00:00:00 2001 From: Chris Pine Date: Mon, 12 Oct 2020 12:15:12 -0700 Subject: [PATCH 3/5] add version checks before gzipping --- internal/api/api.go | 118 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 5 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 167ed2b3c4..84f6772b9f 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -10,10 +10,12 @@ import ( "io/ioutil" "net/http" "os" + "regexp" "strings" + "github.com/Masterminds/semver" "github.com/hashicorp/go-multierror" - "github.com/jig/teereadcloser" + ioaux "github.com/jig/teereadcloser" "github.com/kballard/go-shellquote" "github.com/mattn/go-isatty" "github.com/pkg/errors" @@ -54,7 +56,8 @@ type Request interface { // client is the internal concrete type implementing Client. type client struct { - opts ClientOpts + opts ClientOpts + supportsGzip *bool } // request is the internal concrete type implementing Request. @@ -114,6 +117,35 @@ func (c *client) NewRequest(query string, vars map[string]interface{}) Request { } func (c *client) NewHTTPRequest(ctx context.Context, method, p string, body io.Reader) (*http.Request, error) { + if c.supportsGzip == nil { + version, err := c.getSourcegraphVersion(ctx) + if err != nil { + return nil, err + } + supportsGzip, err := sourcegraphVersionCheck(version, ">= 3.21.0", "2020-10-12") + if err != nil { + return nil, err + } + c.supportsGzip = &supportsGzip + } + + if *c.supportsGzip && body != nil { + body = codeintelutils.Gzip(body) + } + + req, err := c.createHTTPRequest(ctx, method, p, body) + if err != nil { + return nil, err + } + + if *c.supportsGzip { + req.Header.Set("Content-Encoding", "gzip") + } + + return req, nil +} + +func (c *client) createHTTPRequest(ctx context.Context, method, p string, body io.Reader) (*http.Request, error) { req, err := http.NewRequestWithContext(ctx, method, strings.TrimRight(c.opts.Endpoint, "/")+"/"+p, body) if err != nil { return nil, err @@ -127,7 +159,7 @@ func (c *client) NewHTTPRequest(ctx context.Context, method, p string, body io.R for k, v := range c.opts.AdditionalHeaders { req.Header.Set(k, v) } - req.Header.Set("Content-Encoding", "gzip") + return req, nil } @@ -166,8 +198,7 @@ func (r *request) do(ctx context.Context, result interface{}) (bool, error) { } // Create the HTTP request. - zipped := codeintelutils.Gzip(bytes.NewBuffer(reqBody)) - req, err := r.client.NewHTTPRequest(ctx, "POST", ".api/graphql", zipped) + req, err := r.client.NewHTTPRequest(ctx, "POST", ".api/graphql", bytes.NewBuffer(reqBody)) if err != nil { return false, err } @@ -268,3 +299,80 @@ func (r *request) curlCmd() (string, error) { s += fmt.Sprintf(" %s", shellquote.Join(r.client.opts.Endpoint+"/.api/graphql")) return s, nil } + +const sourcegraphVersionQuery = `query SourcegraphVersion { + site { + productVersion + } + } + ` + +func (c *client) getSourcegraphVersion(ctx context.Context) (string, error) { + var sourcegraphVersion struct { + Data struct { + Site struct { + ProductVersion string + } + } + } + + // Create the JSON object. + reqBody, err := json.Marshal(map[string]interface{}{ + "query": sourcegraphVersionQuery, + }) + if err != nil { + return "", err + } + + // Create the HTTP request. + req, err := c.createHTTPRequest(ctx, "POST", ".api/graphql", bytes.NewBuffer(reqBody)) + if err != nil { + return "", err + } + + // Perform the request. + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("checking sourcegraph backend version; got status code %d", resp.StatusCode) + } + + err = json.Unmarshal(respBytes, &sourcegraphVersion) + if err != nil { + return "", err + } + + return sourcegraphVersion.Data.Site.ProductVersion, err +} + +func sourcegraphVersionCheck(version, constraint, minDate string) (bool, error) { + if version == "dev" || version == "0.0.0+dev" { + return true, nil + } + + buildDate := regexp.MustCompile(`^\d+_(\d{4}-\d{2}-\d{2})_[a-z0-9]{7}$`) + matches := buildDate.FindStringSubmatch(version) + if len(matches) > 1 { + return matches[1] >= minDate, nil + } + + c, err := semver.NewConstraint(constraint) + if err != nil { + return false, nil + } + + v, err := semver.NewVersion(version) + if err != nil { + return false, err + } + return c.Check(v), nil +} From ee0794e942093aa25b711d9569152c8f682d2f12 Mon Sep 17 00:00:00 2001 From: Chris Pine Date: Mon, 12 Oct 2020 12:31:48 -0700 Subject: [PATCH 4/5] swallow errors when checking backend version --- internal/api/api.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 84f6772b9f..73ebd82a08 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -118,15 +118,19 @@ func (c *client) NewRequest(query string, vars map[string]interface{}) Request { func (c *client) NewHTTPRequest(ctx context.Context, method, p string, body io.Reader) (*http.Request, error) { if c.supportsGzip == nil { + // set to false, unless we have a new enough version + supportsGzip := false + c.supportsGzip = &supportsGzip + version, err := c.getSourcegraphVersion(ctx) - if err != nil { - return nil, err - } - supportsGzip, err := sourcegraphVersionCheck(version, ">= 3.21.0", "2020-10-12") - if err != nil { - return nil, err + + // ignore errors; we only care if the version is sufficently new + if err == nil { + supportsGzip, err = sourcegraphVersionCheck(version, ">= 3.21.0", "2020-10-12") + if err == nil { + c.supportsGzip = &supportsGzip + } } - c.supportsGzip = &supportsGzip } if *c.supportsGzip && body != nil { From e6f8ed4847fde8e2660dab6b4dc20747dbfa33b7 Mon Sep 17 00:00:00 2001 From: Chris Pine Date: Mon, 12 Oct 2020 12:37:34 -0700 Subject: [PATCH 5/5] I do not know why my editor keeps adding this --- internal/api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/api.go b/internal/api/api.go index 73ebd82a08..c8009b08b5 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -15,7 +15,7 @@ import ( "github.com/Masterminds/semver" "github.com/hashicorp/go-multierror" - ioaux "github.com/jig/teereadcloser" + "github.com/jig/teereadcloser" "github.com/kballard/go-shellquote" "github.com/mattn/go-isatty" "github.com/pkg/errors"