Standardize http.Client creation with User-Agent

This commit is contained in:
Paul Tyng 2018-02-28 11:40:43 -05:00
parent 344e8fca05
commit c868092d2d
No known key found for this signature in database
GPG Key ID: E518875D6C65835A
9 changed files with 146 additions and 28 deletions

View File

@ -13,7 +13,7 @@ import (
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/helper/pathorcontents" "github.com/hashicorp/terraform/helper/pathorcontents"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/httpclient"
"golang.org/x/oauth2/jwt" "golang.org/x/oauth2/jwt"
"google.golang.org/api/option" "google.golang.org/api/option"
) )
@ -156,7 +156,7 @@ func (b *gcsBackend) configure(ctx context.Context) error {
opts = append(opts, option.WithScopes(storage.ScopeReadWrite)) opts = append(opts, option.WithScopes(storage.ScopeReadWrite))
} }
opts = append(opts, option.WithUserAgent(terraform.UserAgentString())) opts = append(opts, option.WithUserAgent(httpclient.UserAgentString()))
client, err := storage.NewClient(b.storageContext, opts...) client, err := storage.NewClient(b.storageContext, opts...)
if err != nil { if err != nil {
return fmt.Errorf("storage.NewClient() failed: %v", err) return fmt.Errorf("storage.NewClient() failed: %v", err)

18
httpclient/client.go Normal file
View File

@ -0,0 +1,18 @@
package httpclient
import (
"net/http"
cleanhttp "github.com/hashicorp/go-cleanhttp"
)
// New returns the DefaultPooledClient from the cleanhttp
// package that will also send a Terraform User-Agent string.
func New() *http.Client {
cli := cleanhttp.DefaultPooledClient()
cli.Transport = &userAgentRoundTripper{
userAgent: UserAgentString(),
inner: cli.Transport,
}
return cli
}

77
httpclient/client_test.go Normal file
View File

@ -0,0 +1,77 @@
package httpclient
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/hashicorp/terraform/version"
)
func TestUserAgent(t *testing.T) {
var actualUserAgent string
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
actualUserAgent = req.UserAgent()
}))
defer ts.Close()
tsURL, err := url.Parse(ts.URL)
if err != nil {
t.Fatal(err)
}
for i, c := range []struct {
expected string
request func(c *http.Client) error
}{
{
fmt.Sprintf("Terraform/%s", version.Version),
func(c *http.Client) error {
_, err := c.Get(ts.URL)
return err
},
},
{
"foo/1",
func(c *http.Client) error {
req := &http.Request{
Method: "GET",
URL: tsURL,
Header: http.Header{
"User-Agent": []string{"foo/1"},
},
}
_, err := c.Do(req)
return err
},
},
{
"",
func(c *http.Client) error {
req := &http.Request{
Method: "GET",
URL: tsURL,
Header: http.Header{
"User-Agent": []string{""},
},
}
_, err := c.Do(req)
return err
},
},
} {
t.Run(fmt.Sprintf("%d %s", i, c.expected), func(t *testing.T) {
actualUserAgent = ""
cli := New()
err := c.request(cli)
if err != nil {
t.Fatal(err)
}
if actualUserAgent != c.expected {
t.Fatalf("actual User-Agent '%s' is not '%s'", actualUserAgent, c.expected)
}
})
}
}

26
httpclient/useragent.go Normal file
View File

@ -0,0 +1,26 @@
package httpclient
import (
"fmt"
"net/http"
"github.com/hashicorp/terraform/version"
)
const userAgentFormat = "Terraform/%s"
func UserAgentString() string {
return fmt.Sprintf(userAgentFormat, version.Version)
}
type userAgentRoundTripper struct {
inner http.RoundTripper
userAgent string
}
func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", rt.userAgent)
}
return rt.inner.RoundTrip(req)
}

View File

@ -15,9 +15,9 @@ import (
"golang.org/x/net/html" "golang.org/x/net/html"
cleanhttp "github.com/hashicorp/go-cleanhttp"
getter "github.com/hashicorp/go-getter" getter "github.com/hashicorp/go-getter"
multierror "github.com/hashicorp/go-multierror" multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/httpclient"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
) )
@ -33,7 +33,19 @@ const protocolVersionHeader = "x-terraform-protocol-version"
var releaseHost = "https://releases.hashicorp.com" var releaseHost = "https://releases.hashicorp.com"
var httpClient = cleanhttp.DefaultPooledClient() var httpClient *http.Client
func init() {
httpClient = httpclient.New()
httpGetter := &getter.HttpGetter{
Client: httpClient,
Netrc: true,
}
getter.Getters["http"] = httpGetter
getter.Getters["https"] = httpGetter
}
// An Installer maintains a local cache of plugins by downloading plugins // An Installer maintains a local cache of plugins by downloading plugins
// from an online repository. // from an online repository.

View File

@ -11,7 +11,7 @@ import (
"strings" "strings"
"time" "time"
cleanhttp "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/terraform/httpclient"
"github.com/hashicorp/terraform/registry/regsrc" "github.com/hashicorp/terraform/registry/regsrc"
"github.com/hashicorp/terraform/registry/response" "github.com/hashicorp/terraform/registry/response"
"github.com/hashicorp/terraform/svchost" "github.com/hashicorp/terraform/svchost"
@ -51,7 +51,7 @@ func NewClient(services *disco.Disco, creds auth.CredentialsSource, client *http
services.SetCredentialsSource(creds) services.SetCredentialsSource(creds)
if client == nil { if client == nil {
client = cleanhttp.DefaultPooledClient() client = httpclient.New()
client.Timeout = requestTimeout client.Timeout = requestTimeout
} }

View File

@ -8,18 +8,16 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"runtime"
"strings" "strings"
"github.com/hashicorp/terraform/helper/pathorcontents" "github.com/hashicorp/terraform/helper/pathorcontents"
"github.com/hashicorp/terraform/httpclient"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt" "golang.org/x/oauth2/jwt"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
"google.golang.org/api/storage/v1" "google.golang.org/api/storage/v1"
version "github.com/hashicorp/terraform/version"
) )
// accountFile represents the structure of the credentials JSON // accountFile represents the structure of the credentials JSON
@ -100,16 +98,13 @@ func gcsFactory(conf map[string]string) (Client, error) {
return nil, err return nil, err
} }
} }
versionString := version.Version
userAgent := fmt.Sprintf(
"(%s %s) Terraform/%s", runtime.GOOS, runtime.GOARCH, versionString)
log.Printf("[INFO] Instantiating Google Storage Client...") log.Printf("[INFO] Instantiating Google Storage Client...")
clientStorage, err := storage.New(client) clientStorage, err := storage.New(client)
if err != nil { if err != nil {
return nil, err return nil, err
} }
clientStorage.UserAgent = userAgent clientStorage.UserAgent = httpclient.UserAgentString()
return &GCSClient{ return &GCSClient{
clientStorage: clientStorage, clientStorage: clientStorage,

View File

@ -8,7 +8,6 @@ package disco
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -20,7 +19,6 @@ import (
cleanhttp "github.com/hashicorp/go-cleanhttp" cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/terraform/svchost" "github.com/hashicorp/terraform/svchost"
"github.com/hashicorp/terraform/svchost/auth" "github.com/hashicorp/terraform/svchost/auth"
"github.com/hashicorp/terraform/version"
) )
const ( const (
@ -30,7 +28,6 @@ const (
maxDiscoDocBytes = 1 * 1024 * 1024 // 1MB - to prevent abusive services from using loads of our memory maxDiscoDocBytes = 1 * 1024 * 1024 // 1MB - to prevent abusive services from using loads of our memory
) )
var userAgent = fmt.Sprintf("Terraform/%s (service discovery)", version.String())
var httpTransport = cleanhttp.DefaultPooledTransport() // overridden during tests, to skip TLS verification var httpTransport = cleanhttp.DefaultPooledTransport() // overridden during tests, to skip TLS verification
// Disco is the main type in this package, which allows discovery on given // Disco is the main type in this package, which allows discovery on given
@ -142,13 +139,9 @@ func (d *Disco) discover(host svchost.Hostname) Host {
}, },
} }
var header = http.Header{}
header.Set("User-Agent", userAgent)
req := &http.Request{ req := &http.Request{
Method: "GET", Method: "GET",
URL: discoURL, URL: discoURL,
Header: header,
} }
if d.credsSrc != nil { if d.credsSrc != nil {

View File

@ -1,16 +1,13 @@
package terraform package terraform
import ( import (
"fmt" "github.com/hashicorp/terraform/httpclient"
"runtime"
"github.com/hashicorp/terraform/version"
) )
// The standard Terraform User-Agent format
const UserAgent = "Terraform %s (%s)"
// Generate a UserAgent string // Generate a UserAgent string
//
// Deprecated: Use httpclient.UserAgentString if you are setting your
// own User-Agent header.
func UserAgentString() string { func UserAgentString() string {
return fmt.Sprintf(UserAgent, version.String(), runtime.Version()) return httpclient.UserAgentString()
} }