dial.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright 2015 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package http supports network connections to HTTP servers.
  15. // This package is not intended for use by end developers. Use the
  16. // google.golang.org/api/option package to configure API clients.
  17. package http
  18. import (
  19. "context"
  20. "errors"
  21. "net/http"
  22. "go.opencensus.io/plugin/ochttp"
  23. "golang.org/x/oauth2"
  24. "google.golang.org/api/googleapi/transport"
  25. "google.golang.org/api/internal"
  26. "google.golang.org/api/option"
  27. "google.golang.org/api/transport/http/internal/propagation"
  28. )
  29. // NewClient returns an HTTP client for use communicating with a Google cloud
  30. // service, configured with the given ClientOptions. It also returns the endpoint
  31. // for the service as specified in the options.
  32. func NewClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) {
  33. settings, err := newSettings(opts)
  34. if err != nil {
  35. return nil, "", err
  36. }
  37. // TODO(cbro): consider injecting the User-Agent even if an explicit HTTP client is provided?
  38. if settings.HTTPClient != nil {
  39. return settings.HTTPClient, settings.Endpoint, nil
  40. }
  41. trans, err := newTransport(ctx, defaultBaseTransport(ctx), settings)
  42. if err != nil {
  43. return nil, "", err
  44. }
  45. return &http.Client{Transport: trans}, settings.Endpoint, nil
  46. }
  47. // NewTransport creates an http.RoundTripper for use communicating with a Google
  48. // cloud service, configured with the given ClientOptions. Its RoundTrip method delegates to base.
  49. func NewTransport(ctx context.Context, base http.RoundTripper, opts ...option.ClientOption) (http.RoundTripper, error) {
  50. settings, err := newSettings(opts)
  51. if err != nil {
  52. return nil, err
  53. }
  54. if settings.HTTPClient != nil {
  55. return nil, errors.New("transport/http: WithHTTPClient passed to NewTransport")
  56. }
  57. return newTransport(ctx, base, settings)
  58. }
  59. func newTransport(ctx context.Context, base http.RoundTripper, settings *internal.DialSettings) (http.RoundTripper, error) {
  60. trans := base
  61. trans = userAgentTransport{
  62. base: trans,
  63. userAgent: settings.UserAgent,
  64. }
  65. trans = addOCTransport(trans)
  66. switch {
  67. case settings.NoAuth:
  68. // Do nothing.
  69. case settings.APIKey != "":
  70. trans = &transport.APIKey{
  71. Transport: trans,
  72. Key: settings.APIKey,
  73. }
  74. default:
  75. creds, err := internal.Creds(ctx, settings)
  76. if err != nil {
  77. return nil, err
  78. }
  79. trans = &oauth2.Transport{
  80. Base: trans,
  81. Source: creds.TokenSource,
  82. }
  83. }
  84. return trans, nil
  85. }
  86. func newSettings(opts []option.ClientOption) (*internal.DialSettings, error) {
  87. var o internal.DialSettings
  88. for _, opt := range opts {
  89. opt.Apply(&o)
  90. }
  91. if err := o.Validate(); err != nil {
  92. return nil, err
  93. }
  94. if o.GRPCConn != nil {
  95. return nil, errors.New("unsupported gRPC connection specified")
  96. }
  97. return &o, nil
  98. }
  99. type userAgentTransport struct {
  100. userAgent string
  101. base http.RoundTripper
  102. }
  103. func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  104. rt := t.base
  105. if rt == nil {
  106. return nil, errors.New("transport: no Transport specified")
  107. }
  108. if t.userAgent == "" {
  109. return rt.RoundTrip(req)
  110. }
  111. newReq := *req
  112. newReq.Header = make(http.Header)
  113. for k, vv := range req.Header {
  114. newReq.Header[k] = vv
  115. }
  116. // TODO(cbro): append to existing User-Agent header?
  117. newReq.Header["User-Agent"] = []string{t.userAgent}
  118. return rt.RoundTrip(&newReq)
  119. }
  120. // Set at init time by dial_appengine.go. If nil, we're not on App Engine.
  121. var appengineUrlfetchHook func(context.Context) http.RoundTripper
  122. // defaultBaseTransport returns the base HTTP transport.
  123. // On App Engine, this is urlfetch.Transport, otherwise it's http.DefaultTransport.
  124. func defaultBaseTransport(ctx context.Context) http.RoundTripper {
  125. if appengineUrlfetchHook != nil {
  126. return appengineUrlfetchHook(ctx)
  127. }
  128. return http.DefaultTransport
  129. }
  130. func addOCTransport(trans http.RoundTripper) http.RoundTripper {
  131. return &ochttp.Transport{
  132. Base: trans,
  133. Propagation: &propagation.HTTPFormat{},
  134. }
  135. }