Skip to content

Commit c20c3f5

Browse files
author
Chris Pine
authored
gzip http requests (#336)
* gzip http requests * remove unused alias * add version checks before gzipping * swallow errors when checking backend version * I do not know why my editor keeps adding this
1 parent 83426b6 commit c20c3f5

File tree

1 file changed

+116
-1
lines changed

1 file changed

+116
-1
lines changed

internal/api/api.go

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ import (
1010
"io/ioutil"
1111
"net/http"
1212
"os"
13+
"regexp"
1314
"strings"
1415

16+
"github.com/Masterminds/semver"
1517
"github.com/hashicorp/go-multierror"
1618
"github.com/jig/teereadcloser"
1719
"github.com/kballard/go-shellquote"
1820
"github.com/mattn/go-isatty"
1921
"github.com/pkg/errors"
22+
"github.com/sourcegraph/codeintelutils"
2023
)
2124

2225
// Client instances provide methods to create API requests.
@@ -53,7 +56,8 @@ type Request interface {
5356

5457
// client is the internal concrete type implementing Client.
5558
type client struct {
56-
opts ClientOpts
59+
opts ClientOpts
60+
supportsGzip *bool
5761
}
5862

5963
// request is the internal concrete type implementing Request.
@@ -113,6 +117,39 @@ func (c *client) NewRequest(query string, vars map[string]interface{}) Request {
113117
}
114118

115119
func (c *client) NewHTTPRequest(ctx context.Context, method, p string, body io.Reader) (*http.Request, error) {
120+
if c.supportsGzip == nil {
121+
// set to false, unless we have a new enough version
122+
supportsGzip := false
123+
c.supportsGzip = &supportsGzip
124+
125+
version, err := c.getSourcegraphVersion(ctx)
126+
127+
// ignore errors; we only care if the version is sufficently new
128+
if err == nil {
129+
supportsGzip, err = sourcegraphVersionCheck(version, ">= 3.21.0", "2020-10-12")
130+
if err == nil {
131+
c.supportsGzip = &supportsGzip
132+
}
133+
}
134+
}
135+
136+
if *c.supportsGzip && body != nil {
137+
body = codeintelutils.Gzip(body)
138+
}
139+
140+
req, err := c.createHTTPRequest(ctx, method, p, body)
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
if *c.supportsGzip {
146+
req.Header.Set("Content-Encoding", "gzip")
147+
}
148+
149+
return req, nil
150+
}
151+
152+
func (c *client) createHTTPRequest(ctx context.Context, method, p string, body io.Reader) (*http.Request, error) {
116153
req, err := http.NewRequestWithContext(ctx, method, strings.TrimRight(c.opts.Endpoint, "/")+"/"+p, body)
117154
if err != nil {
118155
return nil, err
@@ -126,6 +163,7 @@ func (c *client) NewHTTPRequest(ctx context.Context, method, p string, body io.R
126163
for k, v := range c.opts.AdditionalHeaders {
127164
req.Header.Set(k, v)
128165
}
166+
129167
return req, nil
130168
}
131169

@@ -265,3 +303,80 @@ func (r *request) curlCmd() (string, error) {
265303
s += fmt.Sprintf(" %s", shellquote.Join(r.client.opts.Endpoint+"/.api/graphql"))
266304
return s, nil
267305
}
306+
307+
const sourcegraphVersionQuery = `query SourcegraphVersion {
308+
site {
309+
productVersion
310+
}
311+
}
312+
`
313+
314+
func (c *client) getSourcegraphVersion(ctx context.Context) (string, error) {
315+
var sourcegraphVersion struct {
316+
Data struct {
317+
Site struct {
318+
ProductVersion string
319+
}
320+
}
321+
}
322+
323+
// Create the JSON object.
324+
reqBody, err := json.Marshal(map[string]interface{}{
325+
"query": sourcegraphVersionQuery,
326+
})
327+
if err != nil {
328+
return "", err
329+
}
330+
331+
// Create the HTTP request.
332+
req, err := c.createHTTPRequest(ctx, "POST", ".api/graphql", bytes.NewBuffer(reqBody))
333+
if err != nil {
334+
return "", err
335+
}
336+
337+
// Perform the request.
338+
resp, err := http.DefaultClient.Do(req)
339+
if err != nil {
340+
return "", err
341+
}
342+
defer resp.Body.Close()
343+
344+
respBytes, err := ioutil.ReadAll(resp.Body)
345+
if err != nil {
346+
return "", err
347+
}
348+
349+
if resp.StatusCode != http.StatusOK {
350+
return "", fmt.Errorf("checking sourcegraph backend version; got status code %d", resp.StatusCode)
351+
}
352+
353+
err = json.Unmarshal(respBytes, &sourcegraphVersion)
354+
if err != nil {
355+
return "", err
356+
}
357+
358+
return sourcegraphVersion.Data.Site.ProductVersion, err
359+
}
360+
361+
func sourcegraphVersionCheck(version, constraint, minDate string) (bool, error) {
362+
if version == "dev" || version == "0.0.0+dev" {
363+
return true, nil
364+
}
365+
366+
buildDate := regexp.MustCompile(`^\d+_(\d{4}-\d{2}-\d{2})_[a-z0-9]{7}$`)
367+
matches := buildDate.FindStringSubmatch(version)
368+
if len(matches) > 1 {
369+
return matches[1] >= minDate, nil
370+
}
371+
372+
c, err := semver.NewConstraint(constraint)
373+
if err != nil {
374+
return false, nil
375+
}
376+
377+
v, err := semver.NewVersion(version)
378+
if err != nil {
379+
return false, err
380+
}
381+
return c.Check(v), nil
382+
}

0 commit comments

Comments
 (0)