@@ -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.
5558type 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
115119func (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