You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

162 lines
4.7 KiB

  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 = parameterTransport{
  62. base: trans,
  63. userAgent: settings.UserAgent,
  64. quotaProject: settings.QuotaProject,
  65. requestReason: settings.RequestReason,
  66. }
  67. trans = addOCTransport(trans)
  68. switch {
  69. case settings.NoAuth:
  70. // Do nothing.
  71. case settings.APIKey != "":
  72. trans = &transport.APIKey{
  73. Transport: trans,
  74. Key: settings.APIKey,
  75. }
  76. default:
  77. creds, err := internal.Creds(ctx, settings)
  78. if err != nil {
  79. return nil, err
  80. }
  81. trans = &oauth2.Transport{
  82. Base: trans,
  83. Source: creds.TokenSource,
  84. }
  85. }
  86. return trans, nil
  87. }
  88. func newSettings(opts []option.ClientOption) (*internal.DialSettings, error) {
  89. var o internal.DialSettings
  90. for _, opt := range opts {
  91. opt.Apply(&o)
  92. }
  93. if err := o.Validate(); err != nil {
  94. return nil, err
  95. }
  96. if o.GRPCConn != nil {
  97. return nil, errors.New("unsupported gRPC connection specified")
  98. }
  99. return &o, nil
  100. }
  101. type parameterTransport struct {
  102. userAgent string
  103. quotaProject string
  104. requestReason string
  105. base http.RoundTripper
  106. }
  107. func (t parameterTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  108. rt := t.base
  109. if rt == nil {
  110. return nil, errors.New("transport: no Transport specified")
  111. }
  112. if t.userAgent == "" {
  113. return rt.RoundTrip(req)
  114. }
  115. newReq := *req
  116. newReq.Header = make(http.Header)
  117. for k, vv := range req.Header {
  118. newReq.Header[k] = vv
  119. }
  120. // TODO(cbro): append to existing User-Agent header?
  121. newReq.Header.Set("User-Agent", t.userAgent)
  122. // Attach system parameters into the header
  123. if t.quotaProject != "" {
  124. newReq.Header.Set("X-Goog-User-Project", t.quotaProject)
  125. }
  126. if t.requestReason != "" {
  127. newReq.Header.Set("X-Goog-Request-Reason", t.requestReason)
  128. }
  129. return rt.RoundTrip(&newReq)
  130. }
  131. // Set at init time by dial_appengine.go. If nil, we're not on App Engine.
  132. var appengineUrlfetchHook func(context.Context) http.RoundTripper
  133. // defaultBaseTransport returns the base HTTP transport.
  134. // On App Engine, this is urlfetch.Transport, otherwise it's http.DefaultTransport.
  135. func defaultBaseTransport(ctx context.Context) http.RoundTripper {
  136. if appengineUrlfetchHook != nil {
  137. return appengineUrlfetchHook(ctx)
  138. }
  139. return http.DefaultTransport
  140. }
  141. func addOCTransport(trans http.RoundTripper) http.RoundTripper {
  142. return &ochttp.Transport{
  143. Base: trans,
  144. Propagation: &propagation.HTTPFormat{},
  145. }
  146. }