all, transport/http: allow host:port as endpoint override

This change introduces the concept of a "default endpoint", which can be
used for more complicated things involving endpoints.

It also introduces the internaloption package, which are options used
only internally by Google API clients.

Change-Id: I0729a4f7aa8ee9ea0e93747053082541453be7cf
Reviewed-on: https://code-review.googlesource.com/c/google-api-go-client/+/50190
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Andy Zhao <andyzhao@google.com>
Reviewed-by: Tyler Bui-Palsulich <tbp@google.com>
diff --git a/google-api-go-generator/gen.go b/google-api-go-generator/gen.go
index 1902f7b..f4484f3 100644
--- a/google-api-go-generator/gen.go
+++ b/google-api-go-generator/gen.go
@@ -51,10 +51,11 @@
 	baseURL        = flag.String("base_url", "", "(optional) Override the default service API URL. If empty, the service's root URL will be used.")
 	headerPath     = flag.String("header_path", "", "If non-empty, prepend the contents of this file to generated services.")
 
-	gensupportPkg = flag.String("gensupport_pkg", "google.golang.org/api/internal/gensupport", "Go package path of the 'api/internal/gensupport' support package.")
-	googleapiPkg  = flag.String("googleapi_pkg", "google.golang.org/api/googleapi", "Go package path of the 'api/googleapi' support package.")
-	optionPkg     = flag.String("option_pkg", "google.golang.org/api/option", "Go package path of the 'api/option' support package.")
-	htransportPkg = flag.String("htransport_pkg", "google.golang.org/api/transport/http", "Go package path of the 'api/transport/http' support package.")
+	gensupportPkg     = flag.String("gensupport_pkg", "google.golang.org/api/internal/gensupport", "Go package path of the 'api/internal/gensupport' support package.")
+	googleapiPkg      = flag.String("googleapi_pkg", "google.golang.org/api/googleapi", "Go package path of the 'api/googleapi' support package.")
+	optionPkg         = flag.String("option_pkg", "google.golang.org/api/option", "Go package path of the 'api/option' support package.")
+	internalOptionPkg = flag.String("internaloption_pkg", "google.golang.org/api/option/internaloption", "Go package path of the 'api/option/internaloption' support package.")
+	htransportPkg     = flag.String("htransport_pkg", "google.golang.org/api/transport/http", "Go package path of the 'api/transport/http' support package.")
 
 	copyrightYear = flag.String("copyright_year", fmt.Sprintf("%d", time.Now().Year()), "Year for copyright.")
 
@@ -645,6 +646,7 @@
 		{*gensupportPkg, "gensupport"},
 		{*googleapiPkg, "googleapi"},
 		{*optionPkg, "option"},
+		{*internalOptionPkg, "internaloption"},
 		{*htransportPkg, "htransport"},
 	} {
 		pn("  %s %q", imp.lname, imp.pkg)
@@ -663,6 +665,7 @@
 	pn("var _ = errors.New")
 	pn("var _ = strings.Replace")
 	pn("var _ = context.Canceled")
+	pn("var _ = internaloption.WithDefaultEndpoint")
 	pn("")
 	pn("const apiId = %q", a.doc.ID)
 	pn("const apiName = %q", a.doc.Name)
@@ -689,6 +692,7 @@
 		pn("// NOTE: prepend, so we don't override user-specified scopes.")
 		pn("opts = append([]option.ClientOption{scopesOption}, opts...)")
 	}
+	pn("opts = append(opts, internaloption.WithDefaultEndpoint(basePath))")
 	pn("client, endpoint, err := htransport.NewClient(ctx, opts...)")
 	pn("if err != nil { return nil, err }")
 	pn("s, err := New(client)")
diff --git a/google-api-go-generator/gen_test.go b/google-api-go-generator/gen_test.go
index 9f530fe..183b7cb 100644
--- a/google-api-go-generator/gen_test.go
+++ b/google-api-go-generator/gen_test.go
@@ -59,7 +59,9 @@
 			}
 			goldenFile := filepath.Join("testdata", name+".want")
 			if *updateGolden {
-				if err := ioutil.WriteFile(goldenFile, clean, 0644); err != nil {
+				clean := strings.Replace(string(clean), fmt.Sprintf("gl-go/%s", version.Go()), "gl-go/1.12.5", -1)
+				clean = strings.Replace(clean, fmt.Sprintf("gdcl/%s", version.Repo), "gdcl/00000000", -1)
+				if err := ioutil.WriteFile(goldenFile, []byte(clean), 0644); err != nil {
 					t.Fatal(err)
 				}
 			}
diff --git a/google-api-go-generator/testdata/any.want b/google-api-go-generator/testdata/any.want
index c0827f3..90a5a3b 100644
--- a/google-api-go-generator/testdata/any.want
+++ b/google-api-go-generator/testdata/any.want
@@ -52,6 +52,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -68,6 +69,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "logging:v1beta3"
 const apiName = "logging"
@@ -87,6 +89,7 @@
 	)
 	// NOTE: prepend, so we don't override user-specified scopes.
 	opts = append([]option.ClientOption{scopesOption}, opts...)
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/arrayofarray-1.want b/google-api-go-generator/testdata/arrayofarray-1.want
index 8ebb675..b57b562 100644
--- a/google-api-go-generator/testdata/arrayofarray-1.want
+++ b/google-api-go-generator/testdata/arrayofarray-1.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "arrayofarray:v1"
 const apiName = "arrayofarray"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/arrayofenum.want b/google-api-go-generator/testdata/arrayofenum.want
index 4d68a23..2c3d74b 100644
--- a/google-api-go-generator/testdata/arrayofenum.want
+++ b/google-api-go-generator/testdata/arrayofenum.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "arrayofenum:v1"
 const apiName = "arrayofenum"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/arrayofmapofobjects.want b/google-api-go-generator/testdata/arrayofmapofobjects.want
index de7243a..1d95706 100644
--- a/google-api-go-generator/testdata/arrayofmapofobjects.want
+++ b/google-api-go-generator/testdata/arrayofmapofobjects.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "arrayofmapofstrings:v1"
 const apiName = "arrayofmapofstrings"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/arrayofmapofstrings.want b/google-api-go-generator/testdata/arrayofmapofstrings.want
index 2c1f338..afdf924 100644
--- a/google-api-go-generator/testdata/arrayofmapofstrings.want
+++ b/google-api-go-generator/testdata/arrayofmapofstrings.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "arrayofmapofstrings:v1"
 const apiName = "arrayofmapofstrings"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/blogger-3.want b/google-api-go-generator/testdata/blogger-3.want
index 9532427..c081581 100644
--- a/google-api-go-generator/testdata/blogger-3.want
+++ b/google-api-go-generator/testdata/blogger-3.want
@@ -56,6 +56,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -72,6 +73,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "blogger:v3"
 const apiName = "blogger"
@@ -95,6 +97,7 @@
 	)
 	// NOTE: prepend, so we don't override user-specified scopes.
 	opts = append([]option.ClientOption{scopesOption}, opts...)
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/floats.want b/google-api-go-generator/testdata/floats.want
index d12860e..b01d096 100644
--- a/google-api-go-generator/testdata/floats.want
+++ b/google-api-go-generator/testdata/floats.want
@@ -52,6 +52,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -68,6 +69,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "X:v1"
 const apiName = "X"
@@ -76,6 +78,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/getwithoutbody.want b/google-api-go-generator/testdata/getwithoutbody.want
index 448c4ca..c8c1947 100644
--- a/google-api-go-generator/testdata/getwithoutbody.want
+++ b/google-api-go-generator/testdata/getwithoutbody.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "getwithoutbody:v1"
 const apiName = "getwithoutbody"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/http-body.want b/google-api-go-generator/testdata/http-body.want
index 264bf96..432f020 100644
--- a/google-api-go-generator/testdata/http-body.want
+++ b/google-api-go-generator/testdata/http-body.want
@@ -52,6 +52,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -68,6 +69,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "healthcare:v1beta1"
 const apiName = "healthcare"
@@ -87,6 +89,7 @@
 	)
 	// NOTE: prepend, so we don't override user-specified scopes.
 	opts = append([]option.ClientOption{scopesOption}, opts...)
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/json-body.want b/google-api-go-generator/testdata/json-body.want
index c22d106..fac2028 100644
--- a/google-api-go-generator/testdata/json-body.want
+++ b/google-api-go-generator/testdata/json-body.want
@@ -52,6 +52,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -68,6 +69,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "ml:v1"
 const apiName = "ml"
@@ -87,6 +89,7 @@
 	)
 	// NOTE: prepend, so we don't override user-specified scopes.
 	opts = append([]option.ClientOption{scopesOption}, opts...)
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/mapofany.want b/google-api-go-generator/testdata/mapofany.want
index 0ccdc2a..098a2c6 100644
--- a/google-api-go-generator/testdata/mapofany.want
+++ b/google-api-go-generator/testdata/mapofany.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "mapofany:v1"
 const apiName = "mapofany"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/mapofarrayofobjects.want b/google-api-go-generator/testdata/mapofarrayofobjects.want
index 0aac122..99a4469 100644
--- a/google-api-go-generator/testdata/mapofarrayofobjects.want
+++ b/google-api-go-generator/testdata/mapofarrayofobjects.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "additionalprops:v1"
 const apiName = "additionalprops"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/mapofint64strings.want b/google-api-go-generator/testdata/mapofint64strings.want
index 72078e9..9cd76b7 100644
--- a/google-api-go-generator/testdata/mapofint64strings.want
+++ b/google-api-go-generator/testdata/mapofint64strings.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "androidbuildinternal:v1"
 const apiName = "androidbuildinternal"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/mapofobjects.want b/google-api-go-generator/testdata/mapofobjects.want
index 50d7c69..6671abc 100644
--- a/google-api-go-generator/testdata/mapofobjects.want
+++ b/google-api-go-generator/testdata/mapofobjects.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "additionalpropsobjs:v1"
 const apiName = "additionalpropsobjs"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/mapofstrings-1.want b/google-api-go-generator/testdata/mapofstrings-1.want
index 28756d6..96f2e86 100644
--- a/google-api-go-generator/testdata/mapofstrings-1.want
+++ b/google-api-go-generator/testdata/mapofstrings-1.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "additionalprops:v1"
 const apiName = "additionalprops"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/param-rename.want b/google-api-go-generator/testdata/param-rename.want
index 905a287..0d59c86 100644
--- a/google-api-go-generator/testdata/param-rename.want
+++ b/google-api-go-generator/testdata/param-rename.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "paramrename:v1"
 const apiName = "paramrename"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/quotednum.want b/google-api-go-generator/testdata/quotednum.want
index 043fc71..54ac67b 100644
--- a/google-api-go-generator/testdata/quotednum.want
+++ b/google-api-go-generator/testdata/quotednum.want
@@ -52,6 +52,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -68,6 +69,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "adexchangebuyer:v1.1"
 const apiName = "adexchangebuyer"
@@ -87,6 +89,7 @@
 	)
 	// NOTE: prepend, so we don't override user-specified scopes.
 	opts = append([]option.ClientOption{scopesOption}, opts...)
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/repeated.want b/google-api-go-generator/testdata/repeated.want
index a8224f9..e5c7443 100644
--- a/google-api-go-generator/testdata/repeated.want
+++ b/google-api-go-generator/testdata/repeated.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "repeated:v1"
 const apiName = "repeated"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/required-query.want b/google-api-go-generator/testdata/required-query.want
index 6d9e89c..f5631a5 100644
--- a/google-api-go-generator/testdata/required-query.want
+++ b/google-api-go-generator/testdata/required-query.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "tshealth:v1"
 const apiName = "tshealth"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/resource-named-service.want b/google-api-go-generator/testdata/resource-named-service.want
index 3276a81..51308c6 100644
--- a/google-api-go-generator/testdata/resource-named-service.want
+++ b/google-api-go-generator/testdata/resource-named-service.want
@@ -52,6 +52,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -68,6 +69,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "appengine:v1"
 const apiName = "appengine"
@@ -87,6 +89,7 @@
 	)
 	// NOTE: prepend, so we don't override user-specified scopes.
 	opts = append([]option.ClientOption{scopesOption}, opts...)
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/unfortunatedefaults.want b/google-api-go-generator/testdata/unfortunatedefaults.want
index e0e4f7e..b3ff0a3 100644
--- a/google-api-go-generator/testdata/unfortunatedefaults.want
+++ b/google-api-go-generator/testdata/unfortunatedefaults.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "wrapnewlines:v1"
 const apiName = "wrapnewlines"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/variants.want b/google-api-go-generator/testdata/variants.want
index 3a7ddc8..46b3c9f 100644
--- a/google-api-go-generator/testdata/variants.want
+++ b/google-api-go-generator/testdata/variants.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "additionalpropsobjs:v1"
 const apiName = "additionalpropsobjs"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/google-api-go-generator/testdata/wrapnewlines.want b/google-api-go-generator/testdata/wrapnewlines.want
index 76a7cde..efbd467 100644
--- a/google-api-go-generator/testdata/wrapnewlines.want
+++ b/google-api-go-generator/testdata/wrapnewlines.want
@@ -50,6 +50,7 @@
 	googleapi "google.golang.org/api/googleapi"
 	gensupport "google.golang.org/api/internal/gensupport"
 	option "google.golang.org/api/option"
+	internaloption "google.golang.org/api/option/internaloption"
 	htransport "google.golang.org/api/transport/http"
 )
 
@@ -66,6 +67,7 @@
 var _ = errors.New
 var _ = strings.Replace
 var _ = context.Canceled
+var _ = internaloption.WithDefaultEndpoint
 
 const apiId = "wrapnewlines:v1"
 const apiName = "wrapnewlines"
@@ -74,6 +76,7 @@
 
 // NewService creates a new Service.
 func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
 	client, endpoint, err := htransport.NewClient(ctx, opts...)
 	if err != nil {
 		return nil, err
diff --git a/internal/settings.go b/internal/settings.go
index 3af6559..dbd3ec0 100644
--- a/internal/settings.go
+++ b/internal/settings.go
@@ -18,6 +18,7 @@
 // Google API service.
 type DialSettings struct {
 	Endpoint          string
+	DefaultEndpoint   string
 	Scopes            []string
 	TokenSource       oauth2.TokenSource
 	Credentials       *google.Credentials
diff --git a/option/internaloption/internaloption.go b/option/internaloption/internaloption.go
new file mode 100644
index 0000000..48121e4
--- /dev/null
+++ b/option/internaloption/internaloption.go
@@ -0,0 +1,26 @@
+// Copyright 2020 Google LLC.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internaloption contains options used internally by Google client code.
+package internaloption
+
+import (
+	"google.golang.org/api/internal"
+	"google.golang.org/api/option"
+)
+
+type defaultEndpointOption string
+
+func (o defaultEndpointOption) Apply(settings *internal.DialSettings) {
+	settings.DefaultEndpoint = string(o)
+}
+
+// WithDefaultEndpoint is an option that indicates the default endpoint.
+//
+// It should only be used internally by generated clients.
+//
+// This is similar to WithEndpoint, but allows us to determine whether the user has overriden the default endpoint.
+func WithDefaultEndpoint(url string) option.ClientOption {
+	return defaultEndpointOption(url)
+}
diff --git a/transport/http/dial.go b/transport/http/dial.go
index c2ca3b5..642da81 100644
--- a/transport/http/dial.go
+++ b/transport/http/dial.go
@@ -11,6 +11,8 @@
 	"context"
 	"errors"
 	"net/http"
+	"net/url"
+	"strings"
 
 	"go.opencensus.io/plugin/ochttp"
 	"golang.org/x/oauth2"
@@ -28,15 +30,19 @@
 	if err != nil {
 		return nil, "", err
 	}
+	endpoint, err := getEndpoint(settings)
+	if err != nil {
+		return nil, "", err
+	}
 	// TODO(cbro): consider injecting the User-Agent even if an explicit HTTP client is provided?
 	if settings.HTTPClient != nil {
-		return settings.HTTPClient, settings.Endpoint, nil
+		return settings.HTTPClient, endpoint, nil
 	}
 	trans, err := newTransport(ctx, defaultBaseTransport(ctx), settings)
 	if err != nil {
 		return nil, "", err
 	}
-	return &http.Client{Transport: trans}, settings.Endpoint, nil
+	return &http.Client{Transport: trans}, endpoint, nil
 }
 
 // NewTransport creates an http.RoundTripper for use communicating with a Google
@@ -154,3 +160,35 @@
 		Propagation: &propagation.HTTPFormat{},
 	}
 }
+
+// getEndpoint gets the endpoint for the service.
+//
+// If the user-provided endpoint is an address (host:port) rather than full base
+// URL (https://...), then the user-provided address is merged into the default
+// endpoint.
+//
+// For example, (WithEndpoint("myhost:8000"), WithDefaultEndpoint("https://foo.com/bar/baz")) will return "https://myhost:8080/bar/baz"
+func getEndpoint(settings *internal.DialSettings) (string, error) {
+	if settings.Endpoint == "" {
+		return settings.DefaultEndpoint, nil
+	}
+	if strings.Contains(settings.Endpoint, "://") {
+		// User passed in a full URL path, use it verbatim.
+		return settings.Endpoint, nil
+	}
+	if settings.DefaultEndpoint == "" {
+		return "", errors.New("WithEndpoint requires a full URL path")
+	}
+
+	// Assume user-provided endpoint is host[:port], merge it with the default endpoint.
+	return mergeEndpoints(settings.DefaultEndpoint, settings.Endpoint)
+}
+
+func mergeEndpoints(base, newHost string) (string, error) {
+	u, err := url.Parse(base)
+	if err != nil {
+		return "", err
+	}
+	u.Host = newHost
+	return u.String(), nil
+}
diff --git a/transport/http/dial_test.go b/transport/http/dial_test.go
new file mode 100644
index 0000000..f8de30d
--- /dev/null
+++ b/transport/http/dial_test.go
@@ -0,0 +1,58 @@
+// Copyright 2020 Google LLC.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http
+
+import (
+	"testing"
+
+	"google.golang.org/api/internal"
+)
+
+func TestGetEndpoint(t *testing.T) {
+	testCases := []struct {
+		UserEndpoint    string
+		DefaultEndpoint string
+		Want            string
+		WantErr         bool
+	}{
+		{
+			DefaultEndpoint: "https://foo.googleapis.com/bar/baz",
+			Want:            "https://foo.googleapis.com/bar/baz",
+		},
+		{
+			UserEndpoint:    "myhost:3999",
+			DefaultEndpoint: "https://foo.googleapis.com/bar/baz",
+			Want:            "https://myhost:3999/bar/baz",
+		},
+		{
+			UserEndpoint:    "https://host/path/to/bar",
+			DefaultEndpoint: "https://foo.googleapis.com/bar/baz",
+			Want:            "https://host/path/to/bar",
+		},
+		{
+			UserEndpoint:    "host:port",
+			DefaultEndpoint: "",
+			WantErr:         true,
+		},
+	}
+
+	for _, tc := range testCases {
+		got, err := getEndpoint(&internal.DialSettings{
+			Endpoint:        tc.UserEndpoint,
+			DefaultEndpoint: tc.DefaultEndpoint,
+		})
+		if tc.WantErr && err == nil {
+			t.Errorf("want err, got nil err")
+			continue
+		}
+		if !tc.WantErr && err != nil {
+			t.Errorf("want nil err, got %v", err)
+			continue
+		}
+		if tc.Want != got {
+			t.Errorf("getEndpoint(%q, %q): got %v; want %v", tc.UserEndpoint, tc.DefaultEndpoint, got, tc.Want)
+		}
+	}
+}