chore: migrate to gitea
This commit is contained in:
11
vendor/google.golang.org/api/AUTHORS
generated
vendored
Normal file
11
vendor/google.golang.org/api/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# This is the official list of authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
Google Inc.
|
||||
LightStep Inc.
|
||||
56
vendor/google.golang.org/api/CONTRIBUTORS
generated
vendored
Normal file
56
vendor/google.golang.org/api/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# https://cla.developers.google.com/about/google-individual
|
||||
# https://cla.developers.google.com/about/google-corporate
|
||||
#
|
||||
# The CLA can be filled out on the web:
|
||||
#
|
||||
# https://cla.developers.google.com/
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
#
|
||||
# An entry with two email addresses specifies that the
|
||||
# first address should be used in the submit logs and
|
||||
# that the second address should be recognized as the
|
||||
# same person when interacting with Rietveld.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alain Vongsouvanhalainv <alainv@google.com>
|
||||
Andrew Gerrand <adg@golang.org>
|
||||
Brad Fitzpatrick <bradfitz@golang.org>
|
||||
Eric Koleda <ekoleda+devrel@googlers.com>
|
||||
Francesc Campoy <campoy@golang.org>
|
||||
Garrick Evans <garrick@google.com>
|
||||
Glenn Lewis <gmlewis@google.com>
|
||||
Ivan Krasin <krasin@golang.org>
|
||||
Jason Hall <jasonhall@google.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Kostik Shtoyk <kostik@google.com>
|
||||
Kunpei Sakai <namusyaka@gmail.com>
|
||||
Matthew Dolan <dolan@lightstep.com>
|
||||
Matthew Whisenhunt <matt.whisenhunt@gmail.com>
|
||||
Michael McGreevy <mcgreevy@golang.org>
|
||||
Nick Craig-Wood <nickcw@gmail.com>
|
||||
Robbie Trencheny <me@robbiet.us>
|
||||
Ross Light <light@google.com>
|
||||
Sarah Adams <shadams@google.com>
|
||||
Scott Van Woudenberg <scottvw@google.com>
|
||||
Takashi Matsuo <tmatsuo@google.com>
|
||||
27
vendor/google.golang.org/api/LICENSE
generated
vendored
Normal file
27
vendor/google.golang.org/api/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2011 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
552
vendor/google.golang.org/api/googleapi/googleapi.go
generated
vendored
Normal file
552
vendor/google.golang.org/api/googleapi/googleapi.go
generated
vendored
Normal file
@@ -0,0 +1,552 @@
|
||||
// Copyright 2011 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package googleapi contains the common code shared by all Google API
|
||||
// libraries.
|
||||
package googleapi // import "google.golang.org/api/googleapi"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/api/internal/third_party/uritemplates"
|
||||
)
|
||||
|
||||
// ContentTyper is an interface for Readers which know (or would like
|
||||
// to override) their Content-Type. If a media body doesn't implement
|
||||
// ContentTyper, the type is sniffed from the content using
|
||||
// http.DetectContentType.
|
||||
type ContentTyper interface {
|
||||
ContentType() string
|
||||
}
|
||||
|
||||
// A SizeReaderAt is a ReaderAt with a Size method.
|
||||
// An io.SectionReader implements SizeReaderAt.
|
||||
type SizeReaderAt interface {
|
||||
io.ReaderAt
|
||||
Size() int64
|
||||
}
|
||||
|
||||
// ServerResponse is embedded in each Do response and
|
||||
// provides the HTTP status code and header sent by the server.
|
||||
type ServerResponse struct {
|
||||
// HTTPStatusCode is the server's response status code. When using a
|
||||
// resource method's Do call, this will always be in the 2xx range.
|
||||
HTTPStatusCode int
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
const (
|
||||
// Version defines the gax version being used. This is typically sent
|
||||
// in an HTTP header to services.
|
||||
Version = "0.5"
|
||||
|
||||
// UserAgent is the header string used to identify this package.
|
||||
UserAgent = "google-api-go-client/" + Version
|
||||
|
||||
// DefaultUploadChunkSize is the default chunk size to use for resumable
|
||||
// uploads if not specified by the user.
|
||||
DefaultUploadChunkSize = 16 * 1024 * 1024
|
||||
|
||||
// MinUploadChunkSize is the minimum chunk size that can be used for
|
||||
// resumable uploads. All user-specified chunk sizes must be multiple of
|
||||
// this value.
|
||||
MinUploadChunkSize = 256 * 1024
|
||||
)
|
||||
|
||||
// Error contains an error response from the server.
|
||||
type Error struct {
|
||||
// Code is the HTTP response status code and will always be populated.
|
||||
Code int `json:"code"`
|
||||
// Message is the server response message and is only populated when
|
||||
// explicitly referenced by the JSON server response.
|
||||
Message string `json:"message"`
|
||||
// Details provide more context to an error.
|
||||
Details []interface{} `json:"details"`
|
||||
// Body is the raw response returned by the server.
|
||||
// It is often but not always JSON, depending on how the request fails.
|
||||
Body string
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
|
||||
Errors []ErrorItem
|
||||
// err is typically a wrapped apierror.APIError, see
|
||||
// google-api-go-client/internal/gensupport/error.go.
|
||||
err error
|
||||
}
|
||||
|
||||
// ErrorItem is a detailed error code & message from the Google API frontend.
|
||||
type ErrorItem struct {
|
||||
// Reason is the typed error code. For example: "some_example".
|
||||
Reason string `json:"reason"`
|
||||
// Message is the human-readable description of the error.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if len(e.Errors) == 0 && e.Message == "" {
|
||||
return fmt.Sprintf("googleapi: got HTTP response code %d with body: %v", e.Code, e.Body)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "googleapi: Error %d: ", e.Code)
|
||||
if e.Message != "" {
|
||||
fmt.Fprintf(&buf, "%s", e.Message)
|
||||
}
|
||||
if len(e.Details) > 0 {
|
||||
var detailBuf bytes.Buffer
|
||||
enc := json.NewEncoder(&detailBuf)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(e.Details); err == nil {
|
||||
fmt.Fprint(&buf, "\nDetails:")
|
||||
fmt.Fprintf(&buf, "\n%s", detailBuf.String())
|
||||
|
||||
}
|
||||
}
|
||||
if len(e.Errors) == 0 {
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
if len(e.Errors) == 1 && e.Errors[0].Message == e.Message {
|
||||
fmt.Fprintf(&buf, ", %s", e.Errors[0].Reason)
|
||||
return buf.String()
|
||||
}
|
||||
fmt.Fprintln(&buf, "\nMore details:")
|
||||
for _, v := range e.Errors {
|
||||
fmt.Fprintf(&buf, "Reason: %s, Message: %s\n", v.Reason, v.Message)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Wrap allows an existing Error to wrap another error. See also [Error.Unwrap].
|
||||
func (e *Error) Wrap(err error) {
|
||||
e.err = err
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type errorReply struct {
|
||||
Error *Error `json:"error"`
|
||||
}
|
||||
|
||||
// CheckResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx.
|
||||
func CheckResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, err := io.ReadAll(res.Body)
|
||||
if err == nil {
|
||||
return CheckResponseWithBody(res, slurp)
|
||||
}
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
Header: res.Header,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CheckResponseWithBody returns an error (of type *Error) if the response
|
||||
// status code is not 2xx. Distinct from CheckResponse to allow for checking
|
||||
// a previously-read body to maintain error detail content.
|
||||
func CheckResponseWithBody(res *http.Response, body []byte) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
|
||||
jerr, err := errorReplyFromBody(body)
|
||||
if err == nil && jerr.Error != nil {
|
||||
if jerr.Error.Code == 0 {
|
||||
jerr.Error.Code = res.StatusCode
|
||||
}
|
||||
jerr.Error.Body = string(body)
|
||||
jerr.Error.Header = res.Header
|
||||
return jerr.Error
|
||||
}
|
||||
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(body),
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
|
||||
// errorReplyFromBody attempts to get the error from body. The body
|
||||
// may be a JSON object or JSON array, or may be something else.
|
||||
func errorReplyFromBody(body []byte) (*errorReply, error) {
|
||||
jerr := new(errorReply)
|
||||
if len(body) > 0 && body[0] == '[' {
|
||||
// Attempt JSON array
|
||||
jsonArr := []*errorReply{jerr}
|
||||
err := json.Unmarshal(body, &jsonArr)
|
||||
return jerr, err
|
||||
}
|
||||
// Attempt JSON object
|
||||
err := json.Unmarshal(body, jerr)
|
||||
return jerr, err
|
||||
}
|
||||
|
||||
// IsNotModified reports whether err is the result of the
|
||||
// server replying with http.StatusNotModified.
|
||||
// Such error values are sometimes returned by "Do" methods
|
||||
// on calls when If-None-Match is used.
|
||||
func IsNotModified(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ae, ok := err.(*Error)
|
||||
return ok && ae.Code == http.StatusNotModified
|
||||
}
|
||||
|
||||
// CheckMediaResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx. Unlike CheckResponse it does not assume the
|
||||
// body is a JSON error document.
|
||||
// It is the caller's responsibility to close res.Body.
|
||||
func CheckMediaResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, _ := io.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalStyle defines whether to marshal JSON with a {"data": ...} wrapper.
|
||||
type MarshalStyle bool
|
||||
|
||||
// WithDataWrapper marshals JSON with a {"data": ...} wrapper.
|
||||
var WithDataWrapper = MarshalStyle(true)
|
||||
|
||||
// WithoutDataWrapper marshals JSON without a {"data": ...} wrapper.
|
||||
var WithoutDataWrapper = MarshalStyle(false)
|
||||
|
||||
// JSONReader is like JSONBuffer, but returns an io.Reader instead.
|
||||
func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
|
||||
buf, err := wrap.JSONBuffer(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// JSONBuffer encodes the body and wraps it if needed.
|
||||
func (wrap MarshalStyle) JSONBuffer(v interface{}) (*bytes.Buffer, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if wrap {
|
||||
buf.Write([]byte(`{"data": `))
|
||||
}
|
||||
err := json.NewEncoder(buf).Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrap {
|
||||
buf.Write([]byte(`}`))
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// ProgressUpdater is a function that is called upon every progress update of a resumable upload.
|
||||
// This is the only part of a resumable upload (from googleapi) that is usable by the developer.
|
||||
// The remaining usable pieces of resumable uploads is exposed in each auto-generated API.
|
||||
type ProgressUpdater func(current, total int64)
|
||||
|
||||
// MediaOption defines the interface for setting media options.
|
||||
type MediaOption interface {
|
||||
setOptions(o *MediaOptions)
|
||||
}
|
||||
|
||||
type contentTypeOption string
|
||||
|
||||
func (ct contentTypeOption) setOptions(o *MediaOptions) {
|
||||
o.ContentType = string(ct)
|
||||
if o.ContentType == "" {
|
||||
o.ForceEmptyContentType = true
|
||||
}
|
||||
}
|
||||
|
||||
// ContentType returns a MediaOption which sets the Content-Type header for media uploads.
|
||||
// If ctype is empty, the Content-Type header will be omitted.
|
||||
func ContentType(ctype string) MediaOption {
|
||||
return contentTypeOption(ctype)
|
||||
}
|
||||
|
||||
type chunkSizeOption int
|
||||
|
||||
func (cs chunkSizeOption) setOptions(o *MediaOptions) {
|
||||
size := int(cs)
|
||||
if size%MinUploadChunkSize != 0 {
|
||||
size += MinUploadChunkSize - (size % MinUploadChunkSize)
|
||||
}
|
||||
o.ChunkSize = size
|
||||
}
|
||||
|
||||
// ChunkSize returns a MediaOption which sets the chunk size for media uploads.
|
||||
// size will be rounded up to the nearest multiple of 256K.
|
||||
// Media which contains fewer than size bytes will be uploaded in a single request.
|
||||
// Media which contains size bytes or more will be uploaded in separate chunks.
|
||||
// If size is zero, media will be uploaded in a single request.
|
||||
func ChunkSize(size int) MediaOption {
|
||||
return chunkSizeOption(size)
|
||||
}
|
||||
|
||||
type chunkTransferTimeoutOption time.Duration
|
||||
|
||||
func (cd chunkTransferTimeoutOption) setOptions(o *MediaOptions) {
|
||||
o.ChunkTransferTimeout = time.Duration(cd)
|
||||
}
|
||||
|
||||
// ChunkTransferTimeout returns a MediaOption which sets a per-chunk
|
||||
// transfer timeout for resumable uploads. If a single chunk has been
|
||||
// attempting to upload for longer than this time then the old req got canceled and retried.
|
||||
// The default is no timeout for the request.
|
||||
func ChunkTransferTimeout(timeout time.Duration) MediaOption {
|
||||
return chunkTransferTimeoutOption(timeout)
|
||||
}
|
||||
|
||||
type chunkRetryDeadlineOption time.Duration
|
||||
|
||||
func (cd chunkRetryDeadlineOption) setOptions(o *MediaOptions) {
|
||||
o.ChunkRetryDeadline = time.Duration(cd)
|
||||
}
|
||||
|
||||
// ChunkRetryDeadline returns a MediaOption which sets a per-chunk retry
|
||||
// deadline. If a single chunk has been attempting to upload for longer than
|
||||
// this time and the request fails, it will no longer be retried, and the error
|
||||
// will be returned to the caller.
|
||||
// This is only applicable for files which are large enough to require
|
||||
// a multi-chunk resumable upload.
|
||||
// The default value is 32s.
|
||||
// To set a deadline on the entire upload, use context timeout or cancellation.
|
||||
func ChunkRetryDeadline(deadline time.Duration) MediaOption {
|
||||
return chunkRetryDeadlineOption(deadline)
|
||||
}
|
||||
|
||||
type enableAutoChecksumOption struct{}
|
||||
|
||||
func (d enableAutoChecksumOption) setOptions(o *MediaOptions) {
|
||||
o.EnableAutoChecksum = true
|
||||
}
|
||||
|
||||
// EnableAutoChecksum returns a MediaOption that enables automatic checksum
|
||||
// calculation, which is only supported for resumable multi-chunk uploads.
|
||||
// The computed checksum is sent on the final upload request to the server.
|
||||
// Writes are rejected in the event of a checksum mismatch.
|
||||
func EnableAutoChecksum() MediaOption {
|
||||
return enableAutoChecksumOption{}
|
||||
}
|
||||
|
||||
// MediaOptions stores options for customizing media upload. It is not used by developers directly.
|
||||
type MediaOptions struct {
|
||||
ContentType string
|
||||
ForceEmptyContentType bool
|
||||
ChunkSize int
|
||||
ChunkRetryDeadline time.Duration
|
||||
ChunkTransferTimeout time.Duration
|
||||
EnableAutoChecksum bool
|
||||
}
|
||||
|
||||
// ProcessMediaOptions stores options from opts in a MediaOptions.
|
||||
// It is not used by developers directly.
|
||||
func ProcessMediaOptions(opts []MediaOption) *MediaOptions {
|
||||
mo := &MediaOptions{ChunkSize: DefaultUploadChunkSize}
|
||||
for _, o := range opts {
|
||||
o.setOptions(mo)
|
||||
}
|
||||
return mo
|
||||
}
|
||||
|
||||
// ResolveRelative resolves relatives such as "http://www.golang.org/" and
|
||||
// "topics/myproject/mytopic" into a single string, such as
|
||||
// "http://www.golang.org/topics/myproject/mytopic". It strips all parent
|
||||
// references (e.g. ../..) as well as anything after the host
|
||||
// (e.g. /bar/gaz gets stripped out of foo.com/bar/gaz).
|
||||
//
|
||||
// ResolveRelative panics if either basestr or relstr is not able to be parsed.
|
||||
func ResolveRelative(basestr, relstr string) string {
|
||||
u, err := url.Parse(basestr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse %q", basestr))
|
||||
}
|
||||
afterColonPath := ""
|
||||
if i := strings.IndexRune(relstr, ':'); i > 0 {
|
||||
afterColonPath = relstr[i+1:]
|
||||
relstr = relstr[:i]
|
||||
}
|
||||
rel, err := url.Parse(relstr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse %q", relstr))
|
||||
}
|
||||
u = u.ResolveReference(rel)
|
||||
us := u.String()
|
||||
if afterColonPath != "" {
|
||||
us = fmt.Sprintf("%s:%s", us, afterColonPath)
|
||||
}
|
||||
us = strings.Replace(us, "%7B", "{", -1)
|
||||
us = strings.Replace(us, "%7D", "}", -1)
|
||||
us = strings.Replace(us, "%2A", "*", -1)
|
||||
return us
|
||||
}
|
||||
|
||||
// Expand subsitutes any {encoded} strings in the URL passed in using
|
||||
// the map supplied.
|
||||
//
|
||||
// This calls SetOpaque to avoid encoding of the parameters in the URL path.
|
||||
func Expand(u *url.URL, expansions map[string]string) {
|
||||
escaped, unescaped, err := uritemplates.Expand(u.Path, expansions)
|
||||
if err == nil {
|
||||
u.Path = unescaped
|
||||
u.RawPath = escaped
|
||||
}
|
||||
}
|
||||
|
||||
// CloseBody is used to close res.Body.
|
||||
// Prior to calling Close, it also tries to Read a small amount to see an EOF.
|
||||
// Not seeing an EOF can prevent HTTP Transports from reusing connections.
|
||||
func CloseBody(res *http.Response) {
|
||||
if res == nil || res.Body == nil {
|
||||
return
|
||||
}
|
||||
// Justification for 3 byte reads: two for up to "\r\n" after
|
||||
// a JSON/XML document, and then 1 to see EOF if we haven't yet.
|
||||
// TODO(bradfitz): detect Go 1.3+ and skip these reads.
|
||||
// See https://codereview.appspot.com/58240043
|
||||
// and https://codereview.appspot.com/49570044
|
||||
buf := make([]byte, 1)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := res.Body.Read(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
}
|
||||
|
||||
// VariantType returns the type name of the given variant.
|
||||
// If the map doesn't contain the named key or the value is not a []interface{}, "" is returned.
|
||||
// This is used to support "variant" APIs that can return one of a number of different types.
|
||||
func VariantType(t map[string]interface{}) string {
|
||||
s, _ := t["type"].(string)
|
||||
return s
|
||||
}
|
||||
|
||||
// ConvertVariant uses the JSON encoder/decoder to fill in the struct 'dst' with the fields found in variant 'v'.
|
||||
// This is used to support "variant" APIs that can return one of a number of different types.
|
||||
// It reports whether the conversion was successful.
|
||||
func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(v)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return json.Unmarshal(buf.Bytes(), dst) == nil
|
||||
}
|
||||
|
||||
// A Field names a field to be retrieved with a partial response.
|
||||
// https://cloud.google.com/storage/docs/json_api/v1/how-tos/performance
|
||||
//
|
||||
// Partial responses can dramatically reduce the amount of data that must be sent to your application.
|
||||
// In order to request partial responses, you can specify the full list of fields
|
||||
// that your application needs by adding the Fields option to your request.
|
||||
//
|
||||
// Field strings use camelCase with leading lower-case characters to identify fields within the response.
|
||||
//
|
||||
// For example, if your response has a "NextPageToken" and a slice of "Items" with "Id" fields,
|
||||
// you could request just those fields like this:
|
||||
//
|
||||
// svc.Events.List().Fields("nextPageToken", "items/id").Do()
|
||||
//
|
||||
// or if you were also interested in each Item's "Updated" field, you can combine them like this:
|
||||
//
|
||||
// svc.Events.List().Fields("nextPageToken", "items(id,updated)").Do()
|
||||
//
|
||||
// Another way to find field names is through the Google API explorer:
|
||||
// https://developers.google.com/apis-explorer/#p/
|
||||
type Field string
|
||||
|
||||
// CombineFields combines fields into a single string.
|
||||
func CombineFields(s []Field) string {
|
||||
r := make([]string, len(s))
|
||||
for i, v := range s {
|
||||
r[i] = string(v)
|
||||
}
|
||||
return strings.Join(r, ",")
|
||||
}
|
||||
|
||||
// A CallOption is an optional argument to an API call.
|
||||
// It should be treated as an opaque value by users of Google APIs.
|
||||
//
|
||||
// A CallOption is something that configures an API call in a way that is
|
||||
// not specific to that API; for instance, controlling the quota user for
|
||||
// an API call is common across many APIs, and is thus a CallOption.
|
||||
type CallOption interface {
|
||||
Get() (key, value string)
|
||||
}
|
||||
|
||||
// A MultiCallOption is an option argument to an API call and can be passed
|
||||
// anywhere a CallOption is accepted. It additionally supports returning a slice
|
||||
// of values for a given key.
|
||||
type MultiCallOption interface {
|
||||
CallOption
|
||||
GetMulti() (key string, value []string)
|
||||
}
|
||||
|
||||
// QuotaUser returns a CallOption that will set the quota user for a call.
|
||||
// The quota user can be used by server-side applications to control accounting.
|
||||
// It can be an arbitrary string up to 40 characters, and will override UserIP
|
||||
// if both are provided.
|
||||
func QuotaUser(u string) CallOption { return quotaUser(u) }
|
||||
|
||||
type quotaUser string
|
||||
|
||||
func (q quotaUser) Get() (string, string) { return "quotaUser", string(q) }
|
||||
|
||||
// UserIP returns a CallOption that will set the "userIp" parameter of a call.
|
||||
// This should be the IP address of the originating request.
|
||||
func UserIP(ip string) CallOption { return userIP(ip) }
|
||||
|
||||
type userIP string
|
||||
|
||||
func (i userIP) Get() (string, string) { return "userIp", string(i) }
|
||||
|
||||
// Trace returns a CallOption that enables diagnostic tracing for a call.
|
||||
// traceToken is an ID supplied by Google support.
|
||||
func Trace(traceToken string) CallOption { return traceTok(traceToken) }
|
||||
|
||||
type traceTok string
|
||||
|
||||
func (t traceTok) Get() (string, string) { return "trace", "token:" + string(t) }
|
||||
|
||||
type queryParameter struct {
|
||||
key string
|
||||
values []string
|
||||
}
|
||||
|
||||
// QueryParameter allows setting the value(s) of an arbitrary key.
|
||||
func QueryParameter(key string, values ...string) CallOption {
|
||||
return queryParameter{key: key, values: append([]string{}, values...)}
|
||||
}
|
||||
|
||||
// Get will never actually be called -- GetMulti will.
|
||||
func (q queryParameter) Get() (string, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// GetMulti returns the key and values values associated to that key.
|
||||
func (q queryParameter) GetMulti() (string, []string) {
|
||||
return q.key, q.values
|
||||
}
|
||||
|
||||
// TODO: Fields too
|
||||
44
vendor/google.golang.org/api/googleapi/transport/apikey.go
generated
vendored
Normal file
44
vendor/google.golang.org/api/googleapi/transport/apikey.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2012 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package transport contains HTTP transports used to make
|
||||
// authenticated API requests.
|
||||
//
|
||||
// This package is DEPRECATED. Users should instead use,
|
||||
//
|
||||
// service, err := NewService(..., option.WithAPIKey(...))
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// APIKey is an HTTP Transport which wraps an underlying transport and
|
||||
// appends an API Key "key" parameter to the URL of outgoing requests.
|
||||
//
|
||||
// Deprecated: please use NewService(..., option.WithAPIKey(...)) instead.
|
||||
type APIKey struct {
|
||||
// Key is the API Key to set on requests.
|
||||
Key string
|
||||
|
||||
// Transport is the underlying HTTP transport.
|
||||
// If nil, http.DefaultTransport is used.
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *APIKey) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt := t.Transport
|
||||
if rt == nil {
|
||||
rt = http.DefaultTransport
|
||||
if rt == nil {
|
||||
return nil, errors.New("googleapi/transport: no Transport specified or available")
|
||||
}
|
||||
}
|
||||
newReq := *req
|
||||
args := newReq.URL.Query()
|
||||
args.Set("key", t.Key)
|
||||
newReq.URL.RawQuery = args.Encode()
|
||||
return rt.RoundTrip(&newReq)
|
||||
}
|
||||
202
vendor/google.golang.org/api/googleapi/types.go
generated
vendored
Normal file
202
vendor/google.golang.org/api/googleapi/types.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2013 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Int64s is a slice of int64s that marshal as quoted strings in JSON.
|
||||
type Int64s []int64
|
||||
|
||||
func (q *Int64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, int64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int32s is a slice of int32s that marshal as quoted strings in JSON.
|
||||
type Int32s []int32
|
||||
|
||||
func (q *Int32s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, int32(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64s is a slice of uint64s that marshal as quoted strings in JSON.
|
||||
type Uint64s []uint64
|
||||
|
||||
func (q *Uint64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, uint64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint32s is a slice of uint32s that marshal as quoted strings in JSON.
|
||||
type Uint32s []uint32
|
||||
|
||||
func (q *Uint32s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, uint32(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64s is a slice of float64s that marshal as quoted strings in JSON.
|
||||
type Float64s []float64
|
||||
|
||||
func (q *Float64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, float64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func quotedList(n int, fn func(dst []byte, i int) []byte) ([]byte, error) {
|
||||
dst := make([]byte, 0, 2+n*10) // somewhat arbitrary
|
||||
dst = append(dst, '[')
|
||||
for i := 0; i < n; i++ {
|
||||
if i > 0 {
|
||||
dst = append(dst, ',')
|
||||
}
|
||||
dst = append(dst, '"')
|
||||
dst = fn(dst, i)
|
||||
dst = append(dst, '"')
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func (q Int64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(q), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendInt(dst, q[i], 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (q Int32s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(q), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendInt(dst, int64(q[i]), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (q Uint64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(q), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendUint(dst, q[i], 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (q Uint32s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(q), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendUint(dst, uint64(q[i]), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (q Float64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(q), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendFloat(dst, q[i], 'g', -1, 64)
|
||||
})
|
||||
}
|
||||
|
||||
// RawMessage is a raw encoded JSON value.
|
||||
// It is identical to json.RawMessage, except it does not suffer from
|
||||
// https://golang.org/issue/14493.
|
||||
type RawMessage []byte
|
||||
|
||||
// MarshalJSON returns m.
|
||||
func (m RawMessage) MarshalJSON() ([]byte, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data.
|
||||
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||
if m == nil {
|
||||
return errors.New("googleapi.RawMessage: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
*m = append((*m)[:0], data...)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||
*/
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value
|
||||
// to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool { return &v }
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int32(v int32) *int32 { return &v }
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int64(v int64) *int64 { return &v }
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float64(v float64) *float64 { return &v }
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint32(v uint32) *uint32 { return &v }
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint64(v uint64) *uint64 { return &v }
|
||||
|
||||
// String is a helper routine that allocates a new string value
|
||||
// to store v and returns a pointer to it.
|
||||
func String(v string) *string { return &v }
|
||||
294
vendor/google.golang.org/api/internal/cba.go
generated
vendored
Normal file
294
vendor/google.golang.org/api/internal/cba.go
generated
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// cba.go (certificate-based access) contains utils for implementing Device Certificate
|
||||
// Authentication according to https://google.aip.dev/auth/4114 and Default Credentials
|
||||
// for Google Cloud Virtual Environments according to https://google.aip.dev/auth/4115.
|
||||
//
|
||||
// The overall logic for DCA is as follows:
|
||||
// 1. If both endpoint override and client certificate are specified, use them as is.
|
||||
// 2. If user does not specify client certificate, we will attempt to use default
|
||||
// client certificate.
|
||||
// 3. If user does not specify endpoint override, we will use defaultMtlsEndpoint if
|
||||
// client certificate is available and defaultEndpoint otherwise.
|
||||
//
|
||||
// Implications of the above logic:
|
||||
// 1. If the user specifies a non-mTLS endpoint override but client certificate is
|
||||
// available, we will pass along the cert anyway and let the server decide what to do.
|
||||
// 2. If the user specifies an mTLS endpoint override but client certificate is not
|
||||
// available, we will not fail-fast, but let backend throw error when connecting.
|
||||
//
|
||||
// If running within Google's cloud environment, and client certificate is not specified
|
||||
// and not available through DCA, we will try mTLS with credentials held by
|
||||
// the Secure Session Agent, which is part of Google's cloud infrastructure.
|
||||
//
|
||||
// We would like to avoid introducing client-side logic that parses whether the
|
||||
// endpoint override is an mTLS url, since the url pattern may change at anytime.
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
|
||||
// Package internal supports the options and transport packages.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/s2a-go"
|
||||
"google.golang.org/api/internal/cert"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
mTLSModeAlways = "always"
|
||||
mTLSModeNever = "never"
|
||||
mTLSModeAuto = "auto"
|
||||
|
||||
// Experimental: if true, the code will try MTLS with S2A as the default for transport security. Default value is false.
|
||||
googleAPIUseS2AEnv = "EXPERIMENTAL_GOOGLE_API_USE_S2A"
|
||||
|
||||
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
|
||||
)
|
||||
|
||||
var (
|
||||
errUniverseNotSupportedMTLS = errors.New("mTLS is not supported in any universe other than googleapis.com")
|
||||
)
|
||||
|
||||
// getClientCertificateSourceAndEndpoint is a convenience function that invokes
|
||||
// getClientCertificateSource and getEndpoint sequentially and returns the client
|
||||
// cert source and endpoint as a tuple.
|
||||
func getClientCertificateSourceAndEndpoint(settings *DialSettings) (cert.Source, string, error) {
|
||||
clientCertSource, err := getClientCertificateSource(settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
endpoint, err := getEndpoint(settings, clientCertSource)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
// TODO(chrisdsmith): https://github.com/googleapis/google-api-go-client/issues/2359
|
||||
if settings.Endpoint == "" && !settings.IsUniverseDomainGDU() && settings.DefaultEndpointTemplate != "" {
|
||||
// TODO(chrisdsmith): https://github.com/googleapis/google-api-go-client/issues/2359
|
||||
// if settings.DefaultEndpointTemplate == "" {
|
||||
// return nil, "", errors.New("internaloption.WithDefaultEndpointTemplate is required if option.WithUniverseDomain is not googleapis.com")
|
||||
// }
|
||||
endpoint = resolvedDefaultEndpoint(settings)
|
||||
}
|
||||
return clientCertSource, endpoint, nil
|
||||
}
|
||||
|
||||
type transportConfig struct {
|
||||
clientCertSource cert.Source // The client certificate source.
|
||||
endpoint string // The corresponding endpoint to use based on client certificate source.
|
||||
s2aAddress string // The S2A address if it can be used, otherwise an empty string.
|
||||
s2aMTLSEndpoint string // The MTLS endpoint to use with S2A.
|
||||
}
|
||||
|
||||
func getTransportConfig(settings *DialSettings) (*transportConfig, error) {
|
||||
clientCertSource, endpoint, err := getClientCertificateSourceAndEndpoint(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultTransportConfig := transportConfig{
|
||||
clientCertSource: clientCertSource,
|
||||
endpoint: endpoint,
|
||||
s2aAddress: "",
|
||||
s2aMTLSEndpoint: "",
|
||||
}
|
||||
|
||||
if !shouldUseS2A(clientCertSource, settings) {
|
||||
return &defaultTransportConfig, nil
|
||||
}
|
||||
if !settings.IsUniverseDomainGDU() {
|
||||
return nil, errUniverseNotSupportedMTLS
|
||||
}
|
||||
|
||||
s2aAddress := GetS2AAddress()
|
||||
if s2aAddress == "" {
|
||||
return &defaultTransportConfig, nil
|
||||
}
|
||||
return &transportConfig{
|
||||
clientCertSource: clientCertSource,
|
||||
endpoint: endpoint,
|
||||
s2aAddress: s2aAddress,
|
||||
s2aMTLSEndpoint: settings.DefaultMTLSEndpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getClientCertificateSource returns a default client certificate source, if
|
||||
// not provided by the user.
|
||||
//
|
||||
// A nil default source can be returned if the source does not exist. Any exceptions
|
||||
// encountered while initializing the default source will be reported as client
|
||||
// error (ex. corrupt metadata file).
|
||||
//
|
||||
// Important Note: For now, the environment variable GOOGLE_API_USE_CLIENT_CERTIFICATE
|
||||
// must be set to "true" to allow certificate to be used (including user provided
|
||||
// certificates). For details, see AIP-4114.
|
||||
func getClientCertificateSource(settings *DialSettings) (cert.Source, error) {
|
||||
if !isClientCertificateEnabled() {
|
||||
return nil, nil
|
||||
} else if settings.ClientCertSource != nil {
|
||||
return settings.ClientCertSource, nil
|
||||
} else {
|
||||
return cert.DefaultSource()
|
||||
}
|
||||
}
|
||||
|
||||
func isClientCertificateEnabled() bool {
|
||||
useClientCert := os.Getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE")
|
||||
// TODO(andyrzhao): Update default to return "true" after DCA feature is fully released.
|
||||
return strings.ToLower(useClientCert) == "true"
|
||||
}
|
||||
|
||||
// getEndpoint returns the endpoint for the service, taking into account the
|
||||
// user-provided endpoint override "settings.Endpoint".
|
||||
//
|
||||
// If no endpoint override is specified, we will either return the default endpoint or
|
||||
// the default mTLS endpoint if a client certificate is available.
|
||||
//
|
||||
// You can override the default endpoint choice (mtls vs. regular) by setting the
|
||||
// GOOGLE_API_USE_MTLS_ENDPOINT environment variable.
|
||||
//
|
||||
// If the endpoint override is an address (host:port) rather than full base
|
||||
// URL (ex. https://...), then the user-provided address will be merged into
|
||||
// the default endpoint. For example, WithEndpoint("myhost:8000") and
|
||||
// WithDefaultEndpoint("https://foo.com/bar/baz") will return "https://myhost:8080/bar/baz"
|
||||
func getEndpoint(settings *DialSettings, clientCertSource cert.Source) (string, error) {
|
||||
if settings.Endpoint == "" {
|
||||
if isMTLS(clientCertSource) {
|
||||
if !settings.IsUniverseDomainGDU() {
|
||||
return "", errUniverseNotSupportedMTLS
|
||||
}
|
||||
return settings.DefaultMTLSEndpoint, nil
|
||||
}
|
||||
return resolvedDefaultEndpoint(settings), nil
|
||||
}
|
||||
if strings.Contains(settings.Endpoint, "://") {
|
||||
// User passed in a full URL path, use it verbatim.
|
||||
return settings.Endpoint, nil
|
||||
}
|
||||
if resolvedDefaultEndpoint(settings) == "" {
|
||||
// If DefaultEndpoint is not configured, use the user provided endpoint verbatim.
|
||||
// This allows a naked "host[:port]" URL to be used with GRPC Direct Path.
|
||||
return settings.Endpoint, nil
|
||||
}
|
||||
|
||||
// Assume user-provided endpoint is host[:port], merge it with the default endpoint.
|
||||
return mergeEndpoints(resolvedDefaultEndpoint(settings), settings.Endpoint)
|
||||
}
|
||||
|
||||
func isMTLS(clientCertSource cert.Source) bool {
|
||||
mtlsMode := getMTLSMode()
|
||||
return mtlsMode == mTLSModeAlways || (clientCertSource != nil && mtlsMode == mTLSModeAuto)
|
||||
}
|
||||
|
||||
// resolvedDefaultEndpoint returns the DefaultEndpointTemplate merged with the
|
||||
// Universe Domain if the DefaultEndpointTemplate is set, otherwise returns the
|
||||
// deprecated DefaultEndpoint value.
|
||||
func resolvedDefaultEndpoint(settings *DialSettings) string {
|
||||
if settings.DefaultEndpointTemplate == "" {
|
||||
return settings.DefaultEndpoint
|
||||
}
|
||||
return strings.Replace(settings.DefaultEndpointTemplate, universeDomainPlaceholder, settings.GetUniverseDomain(), 1)
|
||||
}
|
||||
|
||||
func getMTLSMode() string {
|
||||
mode := os.Getenv("GOOGLE_API_USE_MTLS_ENDPOINT")
|
||||
if mode == "" {
|
||||
mode = os.Getenv("GOOGLE_API_USE_MTLS") // Deprecated.
|
||||
}
|
||||
if mode == "" {
|
||||
return mTLSModeAuto
|
||||
}
|
||||
return strings.ToLower(mode)
|
||||
}
|
||||
|
||||
func mergeEndpoints(baseURL, newHost string) (string, error) {
|
||||
u, err := url.Parse(fixScheme(baseURL))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Replace(baseURL, u.Host, newHost, 1), nil
|
||||
}
|
||||
|
||||
func fixScheme(baseURL string) string {
|
||||
if !strings.Contains(baseURL, "://") {
|
||||
return "https://" + baseURL
|
||||
}
|
||||
return baseURL
|
||||
}
|
||||
|
||||
// GetGRPCTransportConfigAndEndpoint returns an instance of credentials.TransportCredentials, and the
|
||||
// corresponding endpoint to use for GRPC client.
|
||||
func GetGRPCTransportConfigAndEndpoint(settings *DialSettings) (credentials.TransportCredentials, string, error) {
|
||||
config, err := getTransportConfig(settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
defaultTransportCreds := credentials.NewTLS(&tls.Config{
|
||||
GetClientCertificate: config.clientCertSource,
|
||||
})
|
||||
if config.s2aAddress == "" {
|
||||
return defaultTransportCreds, config.endpoint, nil
|
||||
}
|
||||
|
||||
s2aTransportCreds, err := s2a.NewClientCreds(&s2a.ClientOptions{
|
||||
S2AAddress: config.s2aAddress,
|
||||
})
|
||||
if err != nil {
|
||||
// Use default if we cannot initialize S2A client transport credentials.
|
||||
return defaultTransportCreds, config.endpoint, nil
|
||||
}
|
||||
return s2aTransportCreds, config.s2aMTLSEndpoint, nil
|
||||
}
|
||||
|
||||
// GetHTTPTransportConfigAndEndpoint returns a client certificate source, a function for dialing MTLS with S2A,
|
||||
// and the endpoint to use for HTTP client.
|
||||
func GetHTTPTransportConfigAndEndpoint(settings *DialSettings) (cert.Source, func(context.Context, string, string) (net.Conn, error), string, error) {
|
||||
config, err := getTransportConfig(settings)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
if config.s2aAddress == "" {
|
||||
return config.clientCertSource, nil, config.endpoint, nil
|
||||
}
|
||||
|
||||
dialTLSContextFunc := s2a.NewS2ADialTLSContextFunc(&s2a.ClientOptions{
|
||||
S2AAddress: config.s2aAddress,
|
||||
})
|
||||
return nil, dialTLSContextFunc, config.s2aMTLSEndpoint, nil
|
||||
}
|
||||
|
||||
func shouldUseS2A(clientCertSource cert.Source, settings *DialSettings) bool {
|
||||
// If client cert is found, use that over S2A.
|
||||
if clientCertSource != nil {
|
||||
return false
|
||||
}
|
||||
// If EXPERIMENTAL_GOOGLE_API_USE_S2A is not set to true, skip S2A.
|
||||
if !isGoogleS2AEnabled() {
|
||||
return false
|
||||
}
|
||||
// If DefaultMTLSEndpoint is not set or has endpoint override, skip S2A.
|
||||
if settings.DefaultMTLSEndpoint == "" || settings.Endpoint != "" {
|
||||
return false
|
||||
}
|
||||
// If custom HTTP client is provided, skip S2A.
|
||||
if settings.HTTPClient != nil {
|
||||
return false
|
||||
}
|
||||
return !settings.EnableDirectPath && !settings.EnableDirectPathXds
|
||||
}
|
||||
|
||||
func isGoogleS2AEnabled() bool {
|
||||
return strings.ToLower(os.Getenv(googleAPIUseS2AEnv)) == "true"
|
||||
}
|
||||
58
vendor/google.golang.org/api/internal/cert/default_cert.go
generated
vendored
Normal file
58
vendor/google.golang.org/api/internal/cert/default_cert.go
generated
vendored
Normal file
@@ -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 cert contains certificate tools for Google API clients.
|
||||
// This package is intended to be used with crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// The certificates can be used to satisfy Google's Endpoint Validation.
|
||||
// See https://cloud.google.com/endpoint-verification/docs/overview
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// defaultCertData holds all the variables pertaining to
|
||||
// the default certficate source created by DefaultSource.
|
||||
//
|
||||
// A singleton model is used to allow the source to be reused
|
||||
// by the transport layer.
|
||||
type defaultCertData struct {
|
||||
once sync.Once
|
||||
source Source
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCert defaultCertData
|
||||
)
|
||||
|
||||
// Source is a function that can be passed into crypto/tls.Config.GetClientCertificate.
|
||||
type Source func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||
|
||||
// errSourceUnavailable is a sentinel error to indicate certificate source is unavailable.
|
||||
var errSourceUnavailable = errors.New("certificate source is unavailable")
|
||||
|
||||
// DefaultSource returns a certificate source using the preferred EnterpriseCertificateProxySource.
|
||||
// If EnterpriseCertificateProxySource is not available, fall back to the legacy SecureConnectSource.
|
||||
//
|
||||
// If neither source is available (due to missing configurations), a nil Source and a nil Error are
|
||||
// returned to indicate that a default certificate source is unavailable.
|
||||
func DefaultSource() (Source, error) {
|
||||
defaultCert.once.Do(func() {
|
||||
defaultCert.source, defaultCert.err = NewEnterpriseCertificateProxySource("")
|
||||
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||
defaultCert.source, defaultCert.err = NewSecureConnectSource("")
|
||||
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||
defaultCert.source, defaultCert.err = nil, nil
|
||||
}
|
||||
}
|
||||
})
|
||||
return defaultCert.source, defaultCert.err
|
||||
}
|
||||
54
vendor/google.golang.org/api/internal/cert/enterprise_cert.go
generated
vendored
Normal file
54
vendor/google.golang.org/api/internal/cert/enterprise_cert.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2022 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cert contains certificate tools for Google API clients.
|
||||
// This package is intended to be used with crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// The certificates can be used to satisfy Google's Endpoint Validation.
|
||||
// See https://cloud.google.com/endpoint-verification/docs/overview
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
|
||||
"github.com/googleapis/enterprise-certificate-proxy/client"
|
||||
)
|
||||
|
||||
type ecpSource struct {
|
||||
key *client.Key
|
||||
}
|
||||
|
||||
// NewEnterpriseCertificateProxySource creates a certificate source
|
||||
// using the Enterprise Certificate Proxy client, which delegates
|
||||
// certifcate related operations to an OS-specific "signer binary"
|
||||
// that communicates with the native keystore (ex. keychain on MacOS).
|
||||
//
|
||||
// The configFilePath points to a config file containing relevant parameters
|
||||
// such as the certificate issuer and the location of the signer binary.
|
||||
// If configFilePath is empty, the client will attempt to load the config from
|
||||
// a well-known gcloud location.
|
||||
func NewEnterpriseCertificateProxySource(configFilePath string) (Source, error) {
|
||||
key, err := client.Cred(configFilePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, client.ErrCredUnavailable) {
|
||||
return nil, errSourceUnavailable
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return (&ecpSource{
|
||||
key: key,
|
||||
}).getClientCertificate, nil
|
||||
}
|
||||
|
||||
func (s *ecpSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
var cert tls.Certificate
|
||||
cert.PrivateKey = s.key
|
||||
cert.Certificate = s.key.CertificateChain()
|
||||
return &cert, nil
|
||||
}
|
||||
122
vendor/google.golang.org/api/internal/cert/secureconnect_cert.go
generated
vendored
Normal file
122
vendor/google.golang.org/api/internal/cert/secureconnect_cert.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2022 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cert contains certificate tools for Google API clients.
|
||||
// This package is intended to be used with crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// The certificates can be used to satisfy Google's Endpoint Validation.
|
||||
// See https://cloud.google.com/endpoint-verification/docs/overview
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
metadataPath = ".secureConnect"
|
||||
metadataFile = "context_aware_metadata.json"
|
||||
)
|
||||
|
||||
type secureConnectSource struct {
|
||||
metadata secureConnectMetadata
|
||||
|
||||
// Cache the cert to avoid executing helper command repeatedly.
|
||||
cachedCertMutex sync.Mutex
|
||||
cachedCert *tls.Certificate
|
||||
}
|
||||
|
||||
type secureConnectMetadata struct {
|
||||
Cmd []string `json:"cert_provider_command"`
|
||||
}
|
||||
|
||||
// NewSecureConnectSource creates a certificate source using
|
||||
// the Secure Connect Helper and its associated metadata file.
|
||||
//
|
||||
// The configFilePath points to the location of the context aware metadata file.
|
||||
// If configFilePath is empty, use the default context aware metadata location.
|
||||
func NewSecureConnectSource(configFilePath string) (Source, error) {
|
||||
if configFilePath == "" {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
// Error locating the default config means Secure Connect is not supported.
|
||||
return nil, errSourceUnavailable
|
||||
}
|
||||
configFilePath = filepath.Join(user.HomeDir, metadataPath, metadataFile)
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(configFilePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// Config file missing means Secure Connect is not supported.
|
||||
return nil, errSourceUnavailable
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var metadata secureConnectMetadata
|
||||
if err := json.Unmarshal(file, &metadata); err != nil {
|
||||
return nil, fmt.Errorf("cert: could not parse JSON in %q: %w", configFilePath, err)
|
||||
}
|
||||
if err := validateMetadata(metadata); err != nil {
|
||||
return nil, fmt.Errorf("cert: invalid config in %q: %w", configFilePath, err)
|
||||
}
|
||||
return (&secureConnectSource{
|
||||
metadata: metadata,
|
||||
}).getClientCertificate, nil
|
||||
}
|
||||
|
||||
func validateMetadata(metadata secureConnectMetadata) error {
|
||||
if len(metadata.Cmd) == 0 {
|
||||
return errors.New("empty cert_provider_command")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *secureConnectSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
s.cachedCertMutex.Lock()
|
||||
defer s.cachedCertMutex.Unlock()
|
||||
if s.cachedCert != nil && !isCertificateExpired(s.cachedCert) {
|
||||
return s.cachedCert, nil
|
||||
}
|
||||
// Expand OS environment variables in the cert provider command such as "$HOME".
|
||||
for i := 0; i < len(s.metadata.Cmd); i++ {
|
||||
s.metadata.Cmd[i] = os.ExpandEnv(s.metadata.Cmd[i])
|
||||
}
|
||||
command := s.metadata.Cmd
|
||||
data, err := exec.Command(command[0], command[1:]...).Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := tls.X509KeyPair(data, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.cachedCert = &cert
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
// isCertificateExpired returns true if the given cert is expired or invalid.
|
||||
func isCertificateExpired(cert *tls.Certificate) bool {
|
||||
if len(cert.Certificate) == 0 {
|
||||
return true
|
||||
}
|
||||
parsed, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return time.Now().After(parsed.NotAfter)
|
||||
}
|
||||
30
vendor/google.golang.org/api/internal/conn_pool.go
generated
vendored
Normal file
30
vendor/google.golang.org/api/internal/conn_pool.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 internal
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// ConnPool is a pool of grpc.ClientConns.
|
||||
type ConnPool interface {
|
||||
// Conn returns a ClientConn from the pool.
|
||||
//
|
||||
// Conns aren't returned to the pool.
|
||||
Conn() *grpc.ClientConn
|
||||
|
||||
// Num returns the number of connections in the pool.
|
||||
//
|
||||
// It will always return the same value.
|
||||
Num() int
|
||||
|
||||
// Close closes every ClientConn in the pool.
|
||||
//
|
||||
// The error returned by Close may be a single error or multiple errors.
|
||||
Close() error
|
||||
|
||||
// ConnPool implements grpc.ClientConnInterface to enable it to be used directly with generated proto stubs.
|
||||
grpc.ClientConnInterface
|
||||
}
|
||||
113
vendor/google.golang.org/api/internal/credentialstype/credentialstype.go
generated
vendored
Normal file
113
vendor/google.golang.org/api/internal/credentialstype/credentialstype.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2024 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package credentialstype defines the CredType used for specifying the type of JSON credentials.
|
||||
package credentialstype
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// CredType specifies the type of JSON credentials.
|
||||
type CredType string
|
||||
|
||||
const (
|
||||
// Unknown represents an unknown JSON file type.
|
||||
Unknown CredType = ""
|
||||
// ServiceAccount represents a service account file type.
|
||||
ServiceAccount CredType = "service_account"
|
||||
// AuthorizedUser represents an authorized user credentials file type.
|
||||
AuthorizedUser CredType = "authorized_user"
|
||||
// ImpersonatedServiceAccount represents an impersonated service account file type.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// This credential type does not validate the credential configuration. A security
|
||||
// risk occurs when a credential configuration configured with malicious urls
|
||||
// is used.
|
||||
// You should validate credential configurations provided by untrusted sources.
|
||||
// See [Security requirements when using credential configurations from an external
|
||||
// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||
// for more details.
|
||||
ImpersonatedServiceAccount CredType = "impersonated_service_account"
|
||||
// ExternalAccount represents an external account file type.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// This credential type does not validate the credential configuration. A security
|
||||
// risk occurs when a credential configuration configured with malicious urls
|
||||
// is used.
|
||||
// You should validate credential configurations provided by untrusted sources.
|
||||
// See [Security requirements when using credential configurations from an external
|
||||
// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||
// for more details.
|
||||
ExternalAccount CredType = "external_account"
|
||||
// GDCHServiceAccount represents a GDCH service account file type.
|
||||
GDCHServiceAccount CredType = "gdc_service_account"
|
||||
// ExternalAccountAuthorizedUser represents an external account authorized user file type.
|
||||
ExternalAccountAuthorizedUser CredType = "external_account_authorized_user"
|
||||
)
|
||||
|
||||
var knownTypes = map[CredType]bool{
|
||||
ServiceAccount: true,
|
||||
AuthorizedUser: true,
|
||||
ImpersonatedServiceAccount: true,
|
||||
ExternalAccount: true,
|
||||
GDCHServiceAccount: true,
|
||||
ExternalAccountAuthorizedUser: true,
|
||||
}
|
||||
|
||||
// GetCredType returns the credentials type or the Unknown type,
|
||||
// or an error for empty data or failure to unmarshal JSON.
|
||||
func GetCredType(data []byte) (CredType, error) {
|
||||
var t CredType
|
||||
if len(data) == 0 {
|
||||
return t, fmt.Errorf("credential provided is 0 bytes")
|
||||
}
|
||||
var f struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &f); err != nil {
|
||||
return t, err
|
||||
}
|
||||
t = parseCredType(f.Type)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// CheckCredentialType checks if the provided JSON bytes match the expected
|
||||
// credential type and, if present, one of the allowed credential types.
|
||||
// An error is returned if the JSON is invalid, the type field is missing,
|
||||
// or the types do not match expected and (if present) allowed.
|
||||
func CheckCredentialType(b []byte, expected CredType, allowed ...CredType) error {
|
||||
var f struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &f); err != nil {
|
||||
return fmt.Errorf("unable to parse credential type: %w", err)
|
||||
}
|
||||
if f.Type == "" {
|
||||
return fmt.Errorf("missing `type` field in credential")
|
||||
}
|
||||
credType := CredType(f.Type)
|
||||
if credType != expected {
|
||||
return fmt.Errorf("credential type mismatch: got %q, expected %q", credType, expected)
|
||||
}
|
||||
if len(allowed) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !slices.Contains(allowed, credType) {
|
||||
return fmt.Errorf("credential type not allowed: %q", credType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseCredType returns the matching CredType for the JSON type string if
|
||||
// it is in the list of publicly exposed types, otherwise Unknown.
|
||||
func parseCredType(typeString string) CredType {
|
||||
ct := CredType(typeString)
|
||||
if knownTypes[ct] {
|
||||
return ct
|
||||
}
|
||||
return Unknown
|
||||
}
|
||||
341
vendor/google.golang.org/api/internal/creds.go
generated
vendored
Normal file
341
vendor/google.golang.org/api/internal/creds.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
// Copyright 2017 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/auth"
|
||||
"cloud.google.com/go/auth/credentials"
|
||||
"cloud.google.com/go/auth/oauth2adapt"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/internal/cert"
|
||||
"google.golang.org/api/internal/credentialstype"
|
||||
"google.golang.org/api/internal/impersonate"
|
||||
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
const quotaProjectEnvVar = "GOOGLE_CLOUD_QUOTA_PROJECT"
|
||||
|
||||
// Creds returns credential information obtained from DialSettings, or if none, then
|
||||
// it returns default credential information.
|
||||
func Creds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) {
|
||||
if ds.IsNewAuthLibraryEnabled() {
|
||||
return credsNewAuth(ds)
|
||||
}
|
||||
creds, err := baseCreds(ctx, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ds.ImpersonationConfig != nil {
|
||||
return impersonateCredentials(ctx, creds, ds)
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// AuthCreds returns [cloud.google.com/go/auth.Credentials] based on credentials
|
||||
// options provided via [option.ClientOption], including legacy oauth2/google
|
||||
// options. If there are no applicable options, then it returns the result of
|
||||
// [cloud.google.com/go/auth/credentials.DetectDefault].
|
||||
// Note: If NoAuth is true, when [google.golang.org/api/option.WithoutAuthentication]
|
||||
// is passed, then no authentication will be performed and this function will
|
||||
// return nil, nil.
|
||||
func AuthCreds(ctx context.Context, settings *DialSettings) (*auth.Credentials, error) {
|
||||
if settings.NoAuth {
|
||||
return nil, nil
|
||||
}
|
||||
if settings.AuthCredentials != nil {
|
||||
return settings.AuthCredentials, nil
|
||||
}
|
||||
// Support oauth2/google options
|
||||
var oauth2Creds *google.Credentials
|
||||
if settings.InternalCredentials != nil {
|
||||
oauth2Creds = settings.InternalCredentials
|
||||
} else if settings.Credentials != nil {
|
||||
oauth2Creds = settings.Credentials
|
||||
} else if settings.TokenSource != nil {
|
||||
oauth2Creds = &google.Credentials{TokenSource: settings.TokenSource}
|
||||
}
|
||||
if oauth2Creds != nil {
|
||||
return oauth2adapt.AuthCredentialsFromOauth2Credentials(oauth2Creds), nil
|
||||
}
|
||||
|
||||
return detectDefaultFromDialSettings(settings)
|
||||
}
|
||||
|
||||
// GetOAuth2Configuration determines configurations for the OAuth2 transport, which is separate from the API transport.
|
||||
// The OAuth2 transport and endpoint will be configured for mTLS if applicable.
|
||||
func GetOAuth2Configuration(ctx context.Context, settings *DialSettings) (string, *http.Client, error) {
|
||||
clientCertSource, err := getClientCertificateSource(settings)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
tokenURL := oAuth2Endpoint(clientCertSource)
|
||||
var oauth2Client *http.Client
|
||||
if clientCertSource != nil {
|
||||
tlsConfig := &tls.Config{
|
||||
GetClientCertificate: clientCertSource,
|
||||
}
|
||||
oauth2Client = customHTTPClient(tlsConfig)
|
||||
} else {
|
||||
oauth2Client = oauth2.NewClient(ctx, nil)
|
||||
}
|
||||
return tokenURL, oauth2Client, nil
|
||||
}
|
||||
|
||||
func credsNewAuth(settings *DialSettings) (*google.Credentials, error) {
|
||||
// Preserve old options behavior
|
||||
if settings.InternalCredentials != nil {
|
||||
return settings.InternalCredentials, nil
|
||||
} else if settings.Credentials != nil {
|
||||
return settings.Credentials, nil
|
||||
} else if settings.TokenSource != nil {
|
||||
return &google.Credentials{TokenSource: settings.TokenSource}, nil
|
||||
}
|
||||
|
||||
if settings.AuthCredentials != nil {
|
||||
return oauth2adapt.Oauth2CredentialsFromAuthCredentials(settings.AuthCredentials), nil
|
||||
}
|
||||
|
||||
creds, err := detectDefaultFromDialSettings(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return oauth2adapt.Oauth2CredentialsFromAuthCredentials(creds), nil
|
||||
}
|
||||
|
||||
func detectDefaultFromDialSettings(settings *DialSettings) (*auth.Credentials, error) {
|
||||
var useSelfSignedJWT bool
|
||||
var aud string
|
||||
var scopes []string
|
||||
// If scoped JWTs are enabled user provided an aud, allow self-signed JWT.
|
||||
if settings.EnableJwtWithScope || len(settings.Audiences) > 0 {
|
||||
useSelfSignedJWT = true
|
||||
}
|
||||
|
||||
if len(settings.Scopes) > 0 {
|
||||
scopes = make([]string, len(settings.Scopes))
|
||||
copy(scopes, settings.Scopes)
|
||||
}
|
||||
if len(settings.Audiences) > 0 {
|
||||
aud = settings.Audiences[0]
|
||||
}
|
||||
// Only default scopes if user did not also set an audience.
|
||||
if len(settings.Scopes) == 0 && aud == "" && len(settings.DefaultScopes) > 0 {
|
||||
scopes = make([]string, len(settings.DefaultScopes))
|
||||
copy(scopes, settings.DefaultScopes)
|
||||
}
|
||||
if len(scopes) == 0 && aud == "" {
|
||||
aud = settings.DefaultAudience
|
||||
}
|
||||
|
||||
credsFile, _ := settings.GetAuthCredentialsFile()
|
||||
credsJSON, _ := settings.GetAuthCredentialsJSON()
|
||||
return credentials.DetectDefault(&credentials.DetectOptions{
|
||||
Scopes: scopes,
|
||||
Audience: aud,
|
||||
CredentialsFile: credsFile,
|
||||
CredentialsJSON: credsJSON,
|
||||
UseSelfSignedJWT: useSelfSignedJWT,
|
||||
Logger: settings.Logger,
|
||||
})
|
||||
}
|
||||
|
||||
func baseCreds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) {
|
||||
if ds.InternalCredentials != nil {
|
||||
return ds.InternalCredentials, nil
|
||||
}
|
||||
if ds.Credentials != nil {
|
||||
return ds.Credentials, nil
|
||||
}
|
||||
if credsJSON, checkCredType := ds.GetAuthCredentialsJSON(); len(credsJSON) > 0 {
|
||||
return credentialsFromJSON(ctx, credsJSON, ds, checkCredType)
|
||||
}
|
||||
if credsFile, checkCredType := ds.GetAuthCredentialsFile(); credsFile != "" {
|
||||
data, err := os.ReadFile(credsFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read credentials file: %v", err)
|
||||
}
|
||||
return credentialsFromJSON(ctx, data, ds, checkCredType)
|
||||
}
|
||||
if ds.TokenSource != nil {
|
||||
return &google.Credentials{TokenSource: ds.TokenSource}, nil
|
||||
}
|
||||
cred, err := google.FindDefaultCredentials(ctx, ds.GetScopes()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cred.JSON) > 0 {
|
||||
return credentialsFromJSON(ctx, cred.JSON, ds, credentialstype.Unknown)
|
||||
}
|
||||
// For GAE and GCE, the JSON is empty so return the default credentials directly.
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
// JSON key file type.
|
||||
const (
|
||||
serviceAccountKey = "service_account"
|
||||
)
|
||||
|
||||
// credentialsFromJSON returns a google.Credentials from the JSON data
|
||||
//
|
||||
// - A self-signed JWT flow will be executed if the following conditions are
|
||||
// met:
|
||||
//
|
||||
// (1) At least one of the following is true:
|
||||
// (a) Scope for self-signed JWT flow is enabled
|
||||
// (b) Audiences are explicitly provided by users
|
||||
// (2) No service account impersontation
|
||||
//
|
||||
// - Otherwise, executes standard OAuth 2.0 flow
|
||||
// More details: google.aip.dev/auth/4111
|
||||
func credentialsFromJSON(ctx context.Context, data []byte, ds *DialSettings, checkCredType credentialstype.CredType) (*google.Credentials, error) {
|
||||
if checkCredType != credentialstype.Unknown {
|
||||
if err := credentialstype.CheckCredentialType(data, checkCredType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var params google.CredentialsParams
|
||||
params.Scopes = ds.GetScopes()
|
||||
|
||||
tokenURL, oauth2Client, err := GetOAuth2Configuration(ctx, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.TokenURL = tokenURL
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, oauth2Client)
|
||||
|
||||
// By default, a standard OAuth 2.0 token source is created
|
||||
cred, err := google.CredentialsFromJSONWithParams(ctx, data, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Override the token source to use self-signed JWT if conditions are met
|
||||
isJWTFlow, err := isSelfSignedJWTFlow(data, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isJWTFlow {
|
||||
ts, err := selfSignedJWTTokenSource(data, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cred.TokenSource = ts
|
||||
}
|
||||
|
||||
return cred, err
|
||||
}
|
||||
|
||||
func oAuth2Endpoint(clientCertSource cert.Source) string {
|
||||
if isMTLS(clientCertSource) {
|
||||
return google.MTLSTokenURL
|
||||
}
|
||||
return google.Endpoint.TokenURL
|
||||
}
|
||||
|
||||
func isSelfSignedJWTFlow(data []byte, ds *DialSettings) (bool, error) {
|
||||
// For non-GDU universe domains, token exchange is impossible and services
|
||||
// must support self-signed JWTs with scopes.
|
||||
if !ds.IsUniverseDomainGDU() {
|
||||
return typeServiceAccount(data)
|
||||
}
|
||||
if (ds.EnableJwtWithScope || ds.HasCustomAudience()) && ds.ImpersonationConfig == nil {
|
||||
return typeServiceAccount(data)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// typeServiceAccount checks if JSON data is for a service account.
|
||||
func typeServiceAccount(data []byte) (bool, error) {
|
||||
var f struct {
|
||||
Type string `json:"type"`
|
||||
// The remaining JSON fields are omitted because they are not used.
|
||||
}
|
||||
if err := json.Unmarshal(data, &f); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return f.Type == serviceAccountKey, nil
|
||||
}
|
||||
|
||||
func selfSignedJWTTokenSource(data []byte, ds *DialSettings) (oauth2.TokenSource, error) {
|
||||
if len(ds.GetScopes()) > 0 && !ds.HasCustomAudience() {
|
||||
// Scopes are preferred in self-signed JWT unless the scope is not available
|
||||
// or a custom audience is used.
|
||||
return google.JWTAccessTokenSourceWithScope(data, ds.GetScopes()...)
|
||||
} else if ds.GetAudience() != "" {
|
||||
// Fallback to audience if scope is not provided
|
||||
return google.JWTAccessTokenSourceFromJSON(data, ds.GetAudience())
|
||||
} else {
|
||||
return nil, errors.New("neither scopes or audience are available for the self-signed JWT")
|
||||
}
|
||||
}
|
||||
|
||||
// GetQuotaProject retrieves quota project with precedence being: client option,
|
||||
// environment variable, creds file.
|
||||
func GetQuotaProject(creds *google.Credentials, clientOpt string) string {
|
||||
if clientOpt != "" {
|
||||
return clientOpt
|
||||
}
|
||||
if env := os.Getenv(quotaProjectEnvVar); env != "" {
|
||||
return env
|
||||
}
|
||||
if creds == nil {
|
||||
return ""
|
||||
}
|
||||
var v struct {
|
||||
QuotaProject string `json:"quota_project_id"`
|
||||
}
|
||||
if err := json.Unmarshal(creds.JSON, &v); err != nil {
|
||||
return ""
|
||||
}
|
||||
return v.QuotaProject
|
||||
}
|
||||
|
||||
func impersonateCredentials(ctx context.Context, creds *google.Credentials, ds *DialSettings) (*google.Credentials, error) {
|
||||
if len(ds.ImpersonationConfig.Scopes) == 0 {
|
||||
ds.ImpersonationConfig.Scopes = ds.GetScopes()
|
||||
}
|
||||
ts, err := impersonate.TokenSource(ctx, creds.TokenSource, ds.ImpersonationConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &google.Credentials{
|
||||
TokenSource: ts,
|
||||
ProjectID: creds.ProjectID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// customHTTPClient constructs an HTTPClient using the provided tlsConfig, to support mTLS.
|
||||
func customHTTPClient(tlsConfig *tls.Config) *http.Client {
|
||||
trans := baseTransport()
|
||||
trans.TLSClientConfig = tlsConfig
|
||||
return &http.Client{Transport: trans}
|
||||
}
|
||||
|
||||
func baseTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
92
vendor/google.golang.org/api/internal/gensupport/buffer.go
generated
vendored
Normal file
92
vendor/google.golang.org/api/internal/gensupport/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
// MediaBuffer buffers data from an io.Reader to support uploading media in
|
||||
// retryable chunks. It should be created with NewMediaBuffer.
|
||||
type MediaBuffer struct {
|
||||
media io.Reader
|
||||
|
||||
chunk []byte // The current chunk which is pending upload. The capacity is the chunk size.
|
||||
err error // Any error generated when populating chunk by reading media.
|
||||
|
||||
// The absolute position of chunk in the underlying media.
|
||||
off int64
|
||||
|
||||
// fullObjectChecksum holds the running checksum of streamed media chunks when automatic checksum
|
||||
// calculation is enabled via enableAutoChecksum.
|
||||
fullObjectChecksum uint32
|
||||
enableAutoChecksum bool
|
||||
}
|
||||
|
||||
var (
|
||||
crc32cTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
)
|
||||
|
||||
// NewMediaBuffer initializes a MediaBuffer.
|
||||
func NewMediaBuffer(media io.Reader, chunkSize int) *MediaBuffer {
|
||||
return &MediaBuffer{media: media, chunk: make([]byte, 0, chunkSize)}
|
||||
}
|
||||
|
||||
// Chunk returns the current buffered chunk, the offset in the underlying media
|
||||
// from which the chunk is drawn, and the size of the chunk.
|
||||
// Successive calls to Chunk return the same chunk between calls to Next.
|
||||
func (mb *MediaBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) {
|
||||
// There may already be data in chunk if Next has not been called since the previous call to Chunk.
|
||||
if mb.err == nil && len(mb.chunk) == 0 {
|
||||
mb.err = mb.loadChunk()
|
||||
}
|
||||
return bytes.NewReader(mb.chunk), mb.off, len(mb.chunk), mb.err
|
||||
}
|
||||
|
||||
// loadChunk will read from media into chunk, up to the capacity of chunk.
|
||||
func (mb *MediaBuffer) loadChunk() error {
|
||||
bufSize := cap(mb.chunk)
|
||||
mb.chunk = mb.chunk[:bufSize]
|
||||
|
||||
read := 0
|
||||
var err error
|
||||
for err == nil && read < bufSize {
|
||||
var n int
|
||||
n, err = mb.media.Read(mb.chunk[read:])
|
||||
read += n
|
||||
}
|
||||
mb.chunk = mb.chunk[:read]
|
||||
if mb.enableAutoChecksum {
|
||||
mb.fullObjectChecksum = crc32.Update(mb.fullObjectChecksum, crc32cTable, mb.chunk)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Next advances to the next chunk, which will be returned by the next call to Chunk.
|
||||
// Calls to Next without a corresponding prior call to Chunk will have no effect.
|
||||
func (mb *MediaBuffer) Next() {
|
||||
mb.off += int64(len(mb.chunk))
|
||||
mb.chunk = mb.chunk[0:0]
|
||||
}
|
||||
|
||||
type readerTyper struct {
|
||||
io.Reader
|
||||
googleapi.ContentTyper
|
||||
}
|
||||
|
||||
// ReaderAtToReader adapts a ReaderAt to be used as a Reader.
|
||||
// If ra implements googleapi.ContentTyper, then the returned reader
|
||||
// will also implement googleapi.ContentTyper, delegating to ra.
|
||||
func ReaderAtToReader(ra io.ReaderAt, size int64) io.Reader {
|
||||
r := io.NewSectionReader(ra, 0, size)
|
||||
if typer, ok := ra.(googleapi.ContentTyper); ok {
|
||||
return readerTyper{r, typer}
|
||||
}
|
||||
return r
|
||||
}
|
||||
10
vendor/google.golang.org/api/internal/gensupport/doc.go
generated
vendored
Normal file
10
vendor/google.golang.org/api/internal/gensupport/doc.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gensupport is an internal implementation detail used by code
|
||||
// generated by the google-api-go-generator tool.
|
||||
//
|
||||
// This package may be modified at any time without regard for backwards
|
||||
// compatibility. It should not be used directly by API users.
|
||||
package gensupport
|
||||
24
vendor/google.golang.org/api/internal/gensupport/error.go
generated
vendored
Normal file
24
vendor/google.golang.org/api/internal/gensupport/error.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/googleapis/gax-go/v2/apierror"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
// WrapError creates an [apierror.APIError] from err, wraps it in err, and
|
||||
// returns err. If err is not a [googleapi.Error] (or a
|
||||
// [google.golang.org/grpc/status.Status]), it returns err without modification.
|
||||
func WrapError(err error) error {
|
||||
var herr *googleapi.Error
|
||||
apiError, ok := apierror.ParseError(err, false)
|
||||
if ok && errors.As(err, &herr) {
|
||||
herr.Wrap(apiError)
|
||||
}
|
||||
return err
|
||||
}
|
||||
236
vendor/google.golang.org/api/internal/gensupport/json.go
generated
vendored
Normal file
236
vendor/google.golang.org/api/internal/gensupport/json.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MarshalJSON returns a JSON encoding of schema containing only selected fields.
|
||||
// A field is selected if any of the following is true:
|
||||
// - it has a non-empty value
|
||||
// - its field name is present in forceSendFields and it is not a nil pointer or nil interface
|
||||
// - its field name is present in nullFields.
|
||||
//
|
||||
// The JSON key for each selected field is taken from the field's json: struct tag.
|
||||
func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) {
|
||||
if len(forceSendFields) == 0 && len(nullFields) == 0 {
|
||||
return json.Marshal(schema)
|
||||
}
|
||||
|
||||
mustInclude := make(map[string]bool)
|
||||
for _, f := range forceSendFields {
|
||||
mustInclude[f] = true
|
||||
}
|
||||
useNull := make(map[string]bool)
|
||||
useNullMaps := make(map[string]map[string]bool)
|
||||
for _, nf := range nullFields {
|
||||
parts := strings.SplitN(nf, ".", 2)
|
||||
field := parts[0]
|
||||
if len(parts) == 1 {
|
||||
useNull[field] = true
|
||||
} else {
|
||||
if useNullMaps[field] == nil {
|
||||
useNullMaps[field] = map[string]bool{}
|
||||
}
|
||||
useNullMaps[field][parts[1]] = true
|
||||
}
|
||||
}
|
||||
|
||||
dataMap, err := schemaToMap(schema, mustInclude, useNull, useNullMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(dataMap)
|
||||
}
|
||||
|
||||
func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
|
||||
m := make(map[string]interface{})
|
||||
s := reflect.ValueOf(schema)
|
||||
st := s.Type()
|
||||
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
jsonTag := st.Field(i).Tag.Get("json")
|
||||
if jsonTag == "" {
|
||||
continue
|
||||
}
|
||||
tag, err := parseJSONTag(jsonTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tag.ignore {
|
||||
continue
|
||||
}
|
||||
|
||||
v := s.Field(i)
|
||||
f := st.Field(i)
|
||||
|
||||
if useNull[f.Name] {
|
||||
if !isEmptyValue(v) {
|
||||
return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name)
|
||||
}
|
||||
m[tag.apiName] = nil
|
||||
continue
|
||||
}
|
||||
|
||||
if !includeField(v, f, mustInclude) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If map fields are explicitly set to null, use a map[string]interface{}.
|
||||
if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil {
|
||||
ms, ok := v.Interface().(map[string]string)
|
||||
if !ok {
|
||||
mi, err := initMapSlow(v, f.Name, useNullMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[tag.apiName] = mi
|
||||
continue
|
||||
}
|
||||
mi := map[string]interface{}{}
|
||||
for k, v := range ms {
|
||||
mi[k] = v
|
||||
}
|
||||
for k := range useNullMaps[f.Name] {
|
||||
mi[k] = nil
|
||||
}
|
||||
m[tag.apiName] = mi
|
||||
continue
|
||||
}
|
||||
|
||||
// nil maps are treated as empty maps.
|
||||
if f.Type.Kind() == reflect.Map && v.IsNil() {
|
||||
m[tag.apiName] = map[string]string{}
|
||||
continue
|
||||
}
|
||||
|
||||
// nil slices are treated as empty slices.
|
||||
if f.Type.Kind() == reflect.Slice && v.IsNil() {
|
||||
m[tag.apiName] = []bool{}
|
||||
continue
|
||||
}
|
||||
|
||||
if tag.stringFormat {
|
||||
m[tag.apiName] = formatAsString(v, f.Type.Kind())
|
||||
} else {
|
||||
m[tag.apiName] = v.Interface()
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// initMapSlow uses reflection to build up a map object. This is slower than
|
||||
// the default behavior so it should be used only as a fallback.
|
||||
func initMapSlow(rv reflect.Value, fieldName string, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
|
||||
mi := map[string]interface{}{}
|
||||
iter := rv.MapRange()
|
||||
for iter.Next() {
|
||||
k, ok := iter.Key().Interface().(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]any", fieldName)
|
||||
}
|
||||
v := iter.Value().Interface()
|
||||
mi[k] = v
|
||||
}
|
||||
for k := range useNullMaps[fieldName] {
|
||||
mi[k] = nil
|
||||
}
|
||||
return mi, nil
|
||||
}
|
||||
|
||||
// formatAsString returns a string representation of v, dereferencing it first if possible.
|
||||
func formatAsString(v reflect.Value, kind reflect.Kind) string {
|
||||
if kind == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", v.Interface())
|
||||
}
|
||||
|
||||
// jsonTag represents a restricted version of the struct tag format used by encoding/json.
|
||||
// It is used to describe the JSON encoding of fields in a Schema struct.
|
||||
type jsonTag struct {
|
||||
apiName string
|
||||
stringFormat bool
|
||||
ignore bool
|
||||
}
|
||||
|
||||
// parseJSONTag parses a restricted version of the struct tag format used by encoding/json.
|
||||
// The format of the tag must match that generated by the Schema.writeSchemaStruct method
|
||||
// in the api generator.
|
||||
func parseJSONTag(val string) (jsonTag, error) {
|
||||
if val == "-" {
|
||||
return jsonTag{ignore: true}, nil
|
||||
}
|
||||
|
||||
var tag jsonTag
|
||||
|
||||
i := strings.Index(val, ",")
|
||||
if i == -1 || val[:i] == "" {
|
||||
return tag, fmt.Errorf("malformed json tag: %s", val)
|
||||
}
|
||||
|
||||
tag = jsonTag{
|
||||
apiName: val[:i],
|
||||
}
|
||||
|
||||
switch val[i+1:] {
|
||||
case "omitempty":
|
||||
case "omitempty,string":
|
||||
tag.stringFormat = true
|
||||
default:
|
||||
return tag, fmt.Errorf("malformed json tag: %s", val)
|
||||
}
|
||||
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// Reports whether the struct field "f" with value "v" should be included in JSON output.
|
||||
func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]bool) bool {
|
||||
// The regular JSON encoding of a nil pointer is "null", which means "delete this field".
|
||||
// Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set.
|
||||
// However, many fields are not pointers, so there would be no way to delete these fields.
|
||||
// Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields.
|
||||
// Deletion will be handled by a separate mechanism.
|
||||
if f.Type.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
// The "any" type is represented as an interface{}. If this interface
|
||||
// is nil, there is no reasonable representation to send. We ignore
|
||||
// these fields, for the same reasons as given above for pointers.
|
||||
if f.Type.Kind() == reflect.Interface && v.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
return mustInclude[f.Name] || !isEmptyValue(v)
|
||||
}
|
||||
|
||||
// isEmptyValue reports whether v is the empty value for its type. This
|
||||
// implementation is based on that of the encoding/json package, but its
|
||||
// correctness does not depend on it being identical. What's important is that
|
||||
// this function return false in situations where v should not be sent as part
|
||||
// of a PATCH operation.
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
47
vendor/google.golang.org/api/internal/gensupport/jsonfloat.go
generated
vendored
Normal file
47
vendor/google.golang.org/api/internal/gensupport/jsonfloat.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2016 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// JSONFloat64 is a float64 that supports proper unmarshaling of special float
|
||||
// values in JSON, according to
|
||||
// https://developers.google.com/protocol-buffers/docs/proto3#json. Although
|
||||
// that is a proto-to-JSON spec, it applies to all Google APIs.
|
||||
//
|
||||
// The jsonpb package
|
||||
// (https://github.com/golang/protobuf/blob/master/jsonpb/jsonpb.go) has
|
||||
// similar functionality, but only for direct translation from proto messages
|
||||
// to JSON.
|
||||
type JSONFloat64 float64
|
||||
|
||||
func (f *JSONFloat64) UnmarshalJSON(data []byte) error {
|
||||
var ff float64
|
||||
if err := json.Unmarshal(data, &ff); err == nil {
|
||||
*f = JSONFloat64(ff)
|
||||
return nil
|
||||
}
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
switch s {
|
||||
case "NaN":
|
||||
ff = math.NaN()
|
||||
case "Infinity":
|
||||
ff = math.Inf(1)
|
||||
case "-Infinity":
|
||||
ff = math.Inf(-1)
|
||||
default:
|
||||
return fmt.Errorf("google.golang.org/api/internal: bad float string %q", s)
|
||||
}
|
||||
*f = JSONFloat64(ff)
|
||||
return nil
|
||||
}
|
||||
return errors.New("google.golang.org/api/internal: data not float or string")
|
||||
}
|
||||
318
vendor/google.golang.org/api/internal/gensupport/media.go
generated
vendored
Normal file
318
vendor/google.golang.org/api/internal/gensupport/media.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
gax "github.com/googleapis/gax-go/v2"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
type typeReader struct {
|
||||
io.Reader
|
||||
typ string
|
||||
}
|
||||
|
||||
// multipartReader combines the contents of multiple readers to create a multipart/related HTTP body.
|
||||
// Close must be called if reads from the multipartReader are abandoned before reaching EOF.
|
||||
type multipartReader struct {
|
||||
pr *io.PipeReader
|
||||
ctype string
|
||||
mu sync.Mutex
|
||||
pipeOpen bool
|
||||
}
|
||||
|
||||
// boundary optionally specifies the MIME boundary
|
||||
func newMultipartReader(parts []typeReader, boundary string) *multipartReader {
|
||||
mp := &multipartReader{pipeOpen: true}
|
||||
var pw *io.PipeWriter
|
||||
mp.pr, pw = io.Pipe()
|
||||
mpw := multipart.NewWriter(pw)
|
||||
if boundary != "" {
|
||||
mpw.SetBoundary(boundary)
|
||||
}
|
||||
mp.ctype = "multipart/related; boundary=" + mpw.Boundary()
|
||||
go func() {
|
||||
for _, part := range parts {
|
||||
w, err := mpw.CreatePart(typeHeader(part.typ))
|
||||
if err != nil {
|
||||
mpw.Close()
|
||||
pw.CloseWithError(fmt.Errorf("googleapi: CreatePart failed: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(w, part.Reader)
|
||||
if err != nil {
|
||||
mpw.Close()
|
||||
pw.CloseWithError(fmt.Errorf("googleapi: Copy failed: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mpw.Close()
|
||||
pw.Close()
|
||||
}()
|
||||
return mp
|
||||
}
|
||||
|
||||
func (mp *multipartReader) Read(data []byte) (n int, err error) {
|
||||
return mp.pr.Read(data)
|
||||
}
|
||||
|
||||
func (mp *multipartReader) Close() error {
|
||||
mp.mu.Lock()
|
||||
if !mp.pipeOpen {
|
||||
mp.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
mp.pipeOpen = false
|
||||
mp.mu.Unlock()
|
||||
return mp.pr.Close()
|
||||
}
|
||||
|
||||
// CombineBodyMedia combines a json body with media content to create a multipart/related HTTP body.
|
||||
// It returns a ReadCloser containing the combined body, and the overall "multipart/related" content type, with random boundary.
|
||||
//
|
||||
// The caller must call Close on the returned ReadCloser if reads are abandoned before reaching EOF.
|
||||
func CombineBodyMedia(body io.Reader, bodyContentType string, media io.Reader, mediaContentType string) (io.ReadCloser, string) {
|
||||
return combineBodyMedia(body, bodyContentType, media, mediaContentType, "")
|
||||
}
|
||||
|
||||
// combineBodyMedia is CombineBodyMedia but with an optional mimeBoundary field.
|
||||
func combineBodyMedia(body io.Reader, bodyContentType string, media io.Reader, mediaContentType, mimeBoundary string) (io.ReadCloser, string) {
|
||||
mp := newMultipartReader([]typeReader{
|
||||
{body, bodyContentType},
|
||||
{media, mediaContentType},
|
||||
}, mimeBoundary)
|
||||
return mp, mp.ctype
|
||||
}
|
||||
|
||||
func typeHeader(contentType string) textproto.MIMEHeader {
|
||||
h := make(textproto.MIMEHeader)
|
||||
if contentType != "" {
|
||||
h.Set("Content-Type", contentType)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// PrepareUpload determines whether the data in the supplied reader should be
|
||||
// uploaded in a single request, or in sequential chunks.
|
||||
// chunkSize is the size of the chunk that media should be split into.
|
||||
//
|
||||
// If chunkSize is zero, media is returned as the first value, and the other
|
||||
// two return values are nil, true.
|
||||
//
|
||||
// Otherwise, a MediaBuffer is returned, along with a bool indicating whether the
|
||||
// contents of media fit in a single chunk.
|
||||
//
|
||||
// After PrepareUpload has been called, media should no longer be used: the
|
||||
// media content should be accessed via one of the return values.
|
||||
func PrepareUpload(media io.Reader, chunkSize int, enableAutoChecksum bool) (r io.Reader, mb *MediaBuffer, singleChunk bool) {
|
||||
if chunkSize == 0 { // do not chunk
|
||||
return media, nil, true
|
||||
}
|
||||
mb = NewMediaBuffer(media, chunkSize)
|
||||
mb.enableAutoChecksum = enableAutoChecksum
|
||||
_, _, _, err := mb.Chunk()
|
||||
// If err is io.EOF, we can upload this in a single request. Otherwise, err is
|
||||
// either nil or a non-EOF error. If it is the latter, then the next call to
|
||||
// mb.Chunk will return the same error. Returning a MediaBuffer ensures that this
|
||||
// error will be handled at some point.
|
||||
return nil, mb, err == io.EOF
|
||||
}
|
||||
|
||||
// MediaInfo holds information for media uploads. It is intended for use by generated
|
||||
// code only.
|
||||
type MediaInfo struct {
|
||||
// At most one of Media and MediaBuffer will be set.
|
||||
media io.Reader
|
||||
buffer *MediaBuffer
|
||||
singleChunk bool
|
||||
mType string
|
||||
size int64 // mediaSize, if known. Used only for calls to progressUpdater_.
|
||||
progressUpdater googleapi.ProgressUpdater
|
||||
chunkRetryDeadline time.Duration
|
||||
chunkTransferTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewInfoFromMedia should be invoked from the Media method of a call. It returns a
|
||||
// MediaInfo populated with chunk size and content type, and a reader or MediaBuffer
|
||||
// if needed.
|
||||
func NewInfoFromMedia(r io.Reader, options []googleapi.MediaOption) *MediaInfo {
|
||||
mi := &MediaInfo{}
|
||||
opts := googleapi.ProcessMediaOptions(options)
|
||||
if !opts.ForceEmptyContentType {
|
||||
mi.mType = opts.ContentType
|
||||
if mi.mType == "" {
|
||||
r, mi.mType = gax.DetermineContentType(r)
|
||||
}
|
||||
}
|
||||
mi.chunkRetryDeadline = opts.ChunkRetryDeadline
|
||||
mi.chunkTransferTimeout = opts.ChunkTransferTimeout
|
||||
mi.media, mi.buffer, mi.singleChunk = PrepareUpload(r, opts.ChunkSize, opts.EnableAutoChecksum)
|
||||
return mi
|
||||
}
|
||||
|
||||
// NewInfoFromResumableMedia should be invoked from the ResumableMedia method of a
|
||||
// call. It returns a MediaInfo using the given reader, size and media type.
|
||||
func NewInfoFromResumableMedia(r io.ReaderAt, size int64, mediaType string) *MediaInfo {
|
||||
rdr := ReaderAtToReader(r, size)
|
||||
mType := mediaType
|
||||
if mType == "" {
|
||||
rdr, mType = gax.DetermineContentType(rdr)
|
||||
}
|
||||
|
||||
return &MediaInfo{
|
||||
size: size,
|
||||
mType: mType,
|
||||
buffer: NewMediaBuffer(rdr, googleapi.DefaultUploadChunkSize),
|
||||
media: nil,
|
||||
singleChunk: false,
|
||||
}
|
||||
}
|
||||
|
||||
// SetProgressUpdater sets the progress updater for the media info.
|
||||
func (mi *MediaInfo) SetProgressUpdater(pu googleapi.ProgressUpdater) {
|
||||
if mi != nil {
|
||||
mi.progressUpdater = pu
|
||||
}
|
||||
}
|
||||
|
||||
// UploadType determines the type of upload: a single request, or a resumable
|
||||
// series of requests.
|
||||
func (mi *MediaInfo) UploadType() string {
|
||||
if mi.singleChunk {
|
||||
return "multipart"
|
||||
}
|
||||
return "resumable"
|
||||
}
|
||||
|
||||
// UploadRequest sets up an HTTP request for media upload. It adds headers
|
||||
// as necessary, and returns a replacement for the body and a function for http.Request.GetBody.
|
||||
func (mi *MediaInfo) UploadRequest(reqHeaders http.Header, body io.Reader) (newBody io.Reader, getBody func() (io.ReadCloser, error), cleanup func()) {
|
||||
if body == nil {
|
||||
body = new(bytes.Buffer)
|
||||
}
|
||||
cleanup = func() {}
|
||||
if mi == nil {
|
||||
return body, nil, cleanup
|
||||
}
|
||||
var media io.Reader
|
||||
if mi.media != nil {
|
||||
// This only happens when the caller has turned off chunking. In that
|
||||
// case, we write all of media in a single non-retryable request.
|
||||
media = mi.media
|
||||
} else if mi.singleChunk {
|
||||
// The data fits in a single chunk, which has now been read into the MediaBuffer.
|
||||
// We obtain that chunk so we can write it in a single request. The request can
|
||||
// be retried because the data is stored in the MediaBuffer.
|
||||
media, _, _, _ = mi.buffer.Chunk()
|
||||
}
|
||||
toCleanup := []io.Closer{}
|
||||
if media != nil {
|
||||
fb := readerFunc(body)
|
||||
fm := readerFunc(media)
|
||||
combined, ctype := CombineBodyMedia(body, "application/json", media, mi.mType)
|
||||
toCleanup = append(toCleanup, combined)
|
||||
if fb != nil && fm != nil {
|
||||
getBody = func() (io.ReadCloser, error) {
|
||||
rb := io.NopCloser(fb())
|
||||
rm := io.NopCloser(fm())
|
||||
var mimeBoundary string
|
||||
if _, params, err := mime.ParseMediaType(ctype); err == nil {
|
||||
mimeBoundary = params["boundary"]
|
||||
}
|
||||
r, _ := combineBodyMedia(rb, "application/json", rm, mi.mType, mimeBoundary)
|
||||
toCleanup = append(toCleanup, r)
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
reqHeaders.Set("Content-Type", ctype)
|
||||
body = combined
|
||||
}
|
||||
if mi.buffer != nil && mi.mType != "" && !mi.singleChunk {
|
||||
// This happens when initiating a resumable upload session.
|
||||
// The initial request contains a JSON body rather than media.
|
||||
// It can be retried with a getBody function that re-creates the request body.
|
||||
fb := readerFunc(body)
|
||||
if fb != nil {
|
||||
getBody = func() (io.ReadCloser, error) {
|
||||
rb := io.NopCloser(fb())
|
||||
toCleanup = append(toCleanup, rb)
|
||||
return rb, nil
|
||||
}
|
||||
}
|
||||
reqHeaders.Set("X-Upload-Content-Type", mi.mType)
|
||||
}
|
||||
// Ensure that any bodies created in getBody are cleaned up.
|
||||
cleanup = func() {
|
||||
for _, closer := range toCleanup {
|
||||
_ = closer.Close()
|
||||
}
|
||||
|
||||
}
|
||||
return body, getBody, cleanup
|
||||
}
|
||||
|
||||
// readerFunc returns a function that always returns an io.Reader that has the same
|
||||
// contents as r, provided that can be done without consuming r. Otherwise, it
|
||||
// returns nil.
|
||||
// See http.NewRequest (in net/http/request.go).
|
||||
func readerFunc(r io.Reader) func() io.Reader {
|
||||
switch r := r.(type) {
|
||||
case *bytes.Buffer:
|
||||
buf := r.Bytes()
|
||||
return func() io.Reader { return bytes.NewReader(buf) }
|
||||
case *bytes.Reader:
|
||||
snapshot := *r
|
||||
return func() io.Reader { r := snapshot; return &r }
|
||||
case *strings.Reader:
|
||||
snapshot := *r
|
||||
return func() io.Reader { r := snapshot; return &r }
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ResumableUpload returns an appropriately configured ResumableUpload value if the
|
||||
// upload is resumable, or nil otherwise.
|
||||
func (mi *MediaInfo) ResumableUpload(locURI string) *ResumableUpload {
|
||||
if mi == nil || mi.singleChunk {
|
||||
return nil
|
||||
}
|
||||
return &ResumableUpload{
|
||||
URI: locURI,
|
||||
Media: mi.buffer,
|
||||
MediaType: mi.mType,
|
||||
Callback: func(curr int64) {
|
||||
if mi.progressUpdater != nil {
|
||||
mi.progressUpdater(curr, mi.size)
|
||||
}
|
||||
},
|
||||
ChunkRetryDeadline: mi.chunkRetryDeadline,
|
||||
ChunkTransferTimeout: mi.chunkTransferTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// SetGetBody sets the GetBody field of req to f. This was once needed
|
||||
// to gracefully support Go 1.7 and earlier which didn't have that
|
||||
// field.
|
||||
//
|
||||
// Deprecated: the code generator no longer uses this as of
|
||||
// 2019-02-19. Nothing else should be calling this anyway, but we
|
||||
// won't delete this immediately; it will be deleted in as early as 6
|
||||
// months.
|
||||
func SetGetBody(req *http.Request, f func() (io.ReadCloser, error)) {
|
||||
req.GetBody = f
|
||||
}
|
||||
78
vendor/google.golang.org/api/internal/gensupport/params.go
generated
vendored
Normal file
78
vendor/google.golang.org/api/internal/gensupport/params.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/internal"
|
||||
)
|
||||
|
||||
// URLParams is a simplified replacement for url.Values
|
||||
// that safely builds up URL parameters for encoding.
|
||||
type URLParams map[string][]string
|
||||
|
||||
// Get returns the first value for the given key, or "".
|
||||
func (u URLParams) Get(key string) string {
|
||||
vs := u[key]
|
||||
if len(vs) == 0 {
|
||||
return ""
|
||||
}
|
||||
return vs[0]
|
||||
}
|
||||
|
||||
// Set sets the key to value.
|
||||
// It replaces any existing values.
|
||||
func (u URLParams) Set(key, value string) {
|
||||
u[key] = []string{value}
|
||||
}
|
||||
|
||||
// SetMulti sets the key to an array of values.
|
||||
// It replaces any existing values.
|
||||
// Note that values must not be modified after calling SetMulti
|
||||
// so the caller is responsible for making a copy if necessary.
|
||||
func (u URLParams) SetMulti(key string, values []string) {
|
||||
u[key] = values
|
||||
}
|
||||
|
||||
// Encode encodes the values into “URL encoded” form
|
||||
// ("bar=baz&foo=quux") sorted by key.
|
||||
func (u URLParams) Encode() string {
|
||||
return url.Values(u).Encode()
|
||||
}
|
||||
|
||||
// SetOptions sets the URL params and any additional `CallOption` or
|
||||
// `MultiCallOption` passed in.
|
||||
func SetOptions(u URLParams, opts ...googleapi.CallOption) {
|
||||
for _, o := range opts {
|
||||
m, ok := o.(googleapi.MultiCallOption)
|
||||
if ok {
|
||||
u.SetMulti(m.GetMulti())
|
||||
continue
|
||||
}
|
||||
u.Set(o.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// SetHeaders sets common headers for all requests. The keyvals header pairs
|
||||
// should have a corresponding value for every key provided. If there is an odd
|
||||
// number of keyvals this method will panic.
|
||||
func SetHeaders(userAgent, contentType string, userHeaders http.Header, keyvals ...string) http.Header {
|
||||
reqHeaders := make(http.Header)
|
||||
reqHeaders.Set("x-goog-api-client", "gl-go/"+GoVersion()+" gdcl/"+internal.Version)
|
||||
for i := 0; i < len(keyvals); i = i + 2 {
|
||||
reqHeaders.Set(keyvals[i], keyvals[i+1])
|
||||
}
|
||||
reqHeaders.Set("User-Agent", userAgent)
|
||||
if contentType != "" {
|
||||
reqHeaders.Set("Content-Type", contentType)
|
||||
}
|
||||
for k, v := range userHeaders {
|
||||
reqHeaders[k] = v
|
||||
}
|
||||
return reqHeaders
|
||||
}
|
||||
356
vendor/google.golang.org/api/internal/gensupport/resumable.go
generated
vendored
Normal file
356
vendor/google.golang.org/api/internal/gensupport/resumable.go
generated
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/api/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
crc32cPrefix = "crc32c"
|
||||
hashHeaderKey = "X-Goog-Hash"
|
||||
)
|
||||
|
||||
// ResumableUpload is used by the generated APIs to provide resumable uploads.
|
||||
// It is not used by developers directly.
|
||||
type ResumableUpload struct {
|
||||
Client *http.Client
|
||||
// URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
|
||||
URI string
|
||||
UserAgent string // User-Agent for header of the request
|
||||
// Media is the object being uploaded.
|
||||
Media *MediaBuffer
|
||||
// MediaType defines the media type, e.g. "image/jpeg".
|
||||
MediaType string
|
||||
|
||||
mu sync.Mutex // guards progress
|
||||
progress int64 // number of bytes uploaded so far
|
||||
|
||||
// Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded.
|
||||
Callback func(int64)
|
||||
|
||||
// Retry optionally configures retries for requests made against the upload.
|
||||
Retry *RetryConfig
|
||||
|
||||
// ChunkRetryDeadline configures the per-chunk deadline after which no further
|
||||
// retries should happen.
|
||||
ChunkRetryDeadline time.Duration
|
||||
|
||||
// ChunkTransferTimeout configures the per-chunk transfer timeout. If a chunk upload stalls for longer than
|
||||
// this duration, the upload will be retried.
|
||||
ChunkTransferTimeout time.Duration
|
||||
|
||||
// Track current request invocation ID and attempt count for retry metrics
|
||||
// and idempotency headers.
|
||||
invocationID string
|
||||
attempts int
|
||||
}
|
||||
|
||||
// Progress returns the number of bytes uploaded at this point.
|
||||
func (rx *ResumableUpload) Progress() int64 {
|
||||
rx.mu.Lock()
|
||||
defer rx.mu.Unlock()
|
||||
return rx.progress
|
||||
}
|
||||
|
||||
// doUploadRequest performs a single HTTP request to upload data.
|
||||
// off specifies the offset in rx.Media from which data is drawn.
|
||||
// size is the number of bytes in data.
|
||||
// final specifies whether data is the final chunk to be uploaded.
|
||||
func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader, off, size int64, final bool) (*http.Response, error) {
|
||||
req, err := http.NewRequest("POST", rx.URI, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.ContentLength = size
|
||||
var contentRange string
|
||||
if final {
|
||||
if size == 0 {
|
||||
contentRange = fmt.Sprintf("bytes */%v", off)
|
||||
} else {
|
||||
contentRange = fmt.Sprintf("bytes %v-%v/%v", off, off+size-1, off+size)
|
||||
}
|
||||
} else {
|
||||
contentRange = fmt.Sprintf("bytes %v-%v/*", off, off+size-1)
|
||||
}
|
||||
req.Header.Set("Content-Range", contentRange)
|
||||
req.Header.Set("Content-Type", rx.MediaType)
|
||||
req.Header.Set("User-Agent", rx.UserAgent)
|
||||
|
||||
// TODO(b/274504690): Consider dropping gccl-invocation-id key since it
|
||||
// duplicates the X-Goog-Gcs-Idempotency-Token header (added in v0.115.0).
|
||||
baseXGoogHeader := "gl-go/" + GoVersion() + " gdcl/" + internal.Version
|
||||
invocationHeader := fmt.Sprintf("gccl-invocation-id/%s gccl-attempt-count/%d", rx.invocationID, rx.attempts)
|
||||
req.Header.Set("X-Goog-Api-Client", strings.Join([]string{baseXGoogHeader, invocationHeader}, " "))
|
||||
|
||||
// Set idempotency token header which is used by GCS uploads.
|
||||
req.Header.Set("X-Goog-Gcs-Idempotency-Token", rx.invocationID)
|
||||
|
||||
// Google's upload endpoint uses status code 308 for a
|
||||
// different purpose than the "308 Permanent Redirect"
|
||||
// since-standardized in RFC 7238. Because of the conflict in
|
||||
// semantics, Google added this new request header which
|
||||
// causes it to not use "308" and instead reply with 200 OK
|
||||
// and sets the upload-specific "X-HTTP-Status-Code-Override:
|
||||
// 308" response header.
|
||||
req.Header.Set("X-GUploader-No-308", "yes")
|
||||
|
||||
// Server accepts checksum only on final request through header.
|
||||
if final && rx.Media.enableAutoChecksum {
|
||||
req.Header.Set(hashHeaderKey, fmt.Sprintf("%v=%v", crc32cPrefix, encodeUint32(rx.Media.fullObjectChecksum)))
|
||||
}
|
||||
|
||||
return SendRequest(ctx, rx.Client, req)
|
||||
}
|
||||
|
||||
func statusResumeIncomplete(resp *http.Response) bool {
|
||||
// This is how the server signals "status resume incomplete"
|
||||
// when X-GUploader-No-308 is set to "yes":
|
||||
return resp != nil && resp.Header.Get("X-Http-Status-Code-Override") == "308"
|
||||
}
|
||||
|
||||
// reportProgress calls a user-supplied callback to report upload progress.
|
||||
// If old==updated, the callback is not called.
|
||||
func (rx *ResumableUpload) reportProgress(old, updated int64) {
|
||||
if updated-old == 0 {
|
||||
return
|
||||
}
|
||||
rx.mu.Lock()
|
||||
rx.progress = updated
|
||||
rx.mu.Unlock()
|
||||
if rx.Callback != nil {
|
||||
rx.Callback(updated)
|
||||
}
|
||||
}
|
||||
|
||||
// transferChunk performs a single HTTP request to upload a single chunk.
|
||||
// It uses a goroutine to perform the upload and a timer to enforce ChunkTransferTimeout.
|
||||
func (rx *ResumableUpload) transferChunk(ctx context.Context, chunk io.Reader, off, size int64, done bool) (*http.Response, error) {
|
||||
// If no timeout is specified, perform the request synchronously without a timer.
|
||||
if rx.ChunkTransferTimeout == 0 {
|
||||
res, err := rx.doUploadRequest(ctx, chunk, off, size, done)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Start a timer for the ChunkTransferTimeout duration.
|
||||
timer := time.NewTimer(rx.ChunkTransferTimeout)
|
||||
|
||||
// A struct to hold the result from the goroutine.
|
||||
type uploadResult struct {
|
||||
res *http.Response
|
||||
err error
|
||||
}
|
||||
|
||||
// A buffered channel to receive the result of the upload.
|
||||
resultCh := make(chan uploadResult, 1)
|
||||
|
||||
// Create a cancellable context for the upload request. This allows us to
|
||||
// abort the request if the timer fires first.
|
||||
rCtx, cancel := context.WithCancel(ctx)
|
||||
// NOTE: We do NOT use `defer cancel()` here. The context must remain valid
|
||||
// for the caller to read the response body of a successful request.
|
||||
// Cancellation is handled manually on timeout paths.
|
||||
|
||||
// Starting the chunk upload in parallel.
|
||||
go func() {
|
||||
res, err := rx.doUploadRequest(rCtx, chunk, off, size, done)
|
||||
resultCh <- uploadResult{res: res, err: err}
|
||||
}()
|
||||
|
||||
// Wait for timer to fire or result channel to have the uploadResult or ctx to be cancelled.
|
||||
select {
|
||||
// Note: Calling cancel() will guarantee that the goroutine finishes,
|
||||
// so these two cases will never block forever on draining the resultCh.
|
||||
case <-ctx.Done():
|
||||
// Context is cancelled for the overall upload.
|
||||
cancel()
|
||||
// Drain resultCh.
|
||||
<-resultCh
|
||||
return nil, ctx.Err()
|
||||
case <-timer.C:
|
||||
// Chunk Transfer timer fired before resultCh so we return context.DeadlineExceeded.
|
||||
cancel()
|
||||
// Drain resultCh.
|
||||
<-resultCh
|
||||
return nil, context.DeadlineExceeded
|
||||
case result := <-resultCh:
|
||||
// Handle the result from the upload.
|
||||
if result.err != nil {
|
||||
return result.res, result.err
|
||||
}
|
||||
return result.res, nil
|
||||
}
|
||||
}
|
||||
|
||||
// uploadChunkWithRetries attempts to upload a single chunk, with retries
|
||||
// within ChunkRetryDeadline if ChunkTransferTimeout is non-zero.
|
||||
func (rx *ResumableUpload) uploadChunkWithRetries(ctx context.Context, chunk io.Reader, off, size int64, done bool) (*http.Response, error) {
|
||||
// Configure error retryable criteria.
|
||||
shouldRetry := rx.Retry.errorFunc()
|
||||
|
||||
// Configure single chunk retry deadline.
|
||||
chunkRetryDeadline := defaultRetryDeadline
|
||||
if rx.ChunkRetryDeadline != 0 {
|
||||
chunkRetryDeadline = rx.ChunkRetryDeadline
|
||||
}
|
||||
|
||||
// Each chunk gets its own initialized-at-zero backoff and invocation ID.
|
||||
bo := rx.Retry.backoff()
|
||||
quitAfterTimer := time.NewTimer(chunkRetryDeadline)
|
||||
defer quitAfterTimer.Stop()
|
||||
rx.attempts = 1
|
||||
rx.invocationID = uuid.New().String()
|
||||
|
||||
var pause time.Duration
|
||||
var resp *http.Response
|
||||
var err error
|
||||
|
||||
// Retry loop for a single chunk.
|
||||
for {
|
||||
// Wait for the backoff period, unless the context is canceled or the
|
||||
// retry deadline is hit.
|
||||
backoffPauseTimer := time.NewTimer(pause)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
backoffPauseTimer.Stop()
|
||||
if err == nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
return resp, err
|
||||
case <-backoffPauseTimer.C:
|
||||
case <-quitAfterTimer.C:
|
||||
backoffPauseTimer.Stop()
|
||||
return resp, err
|
||||
}
|
||||
backoffPauseTimer.Stop()
|
||||
|
||||
// Check for context cancellation or timeout once more. If more than one
|
||||
// case in the select statement above was satisfied at the same time, Go
|
||||
// will choose one arbitrarily.
|
||||
// That can cause an operation to go through even if the context was
|
||||
// canceled before or the timeout was reached.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if err == nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
return resp, err
|
||||
case <-quitAfterTimer.C:
|
||||
return resp, err
|
||||
default:
|
||||
}
|
||||
|
||||
// We close the response's body here, since we definitely will not
|
||||
// return `resp` now. If we close it before the select case above, a
|
||||
// timer may fire and cause us to return a response with a closed body
|
||||
// (in which case, the caller will not get the error message in the body).
|
||||
if resp != nil && resp.Body != nil {
|
||||
// Read the body to EOF - if the Body is not both read to EOF and closed,
|
||||
// the Client's underlying RoundTripper may not be able to re-use the
|
||||
// persistent TCP connection to the server for a subsequent "keep-alive" request.
|
||||
// See https://pkg.go.dev/net/http#Client.Do
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
resp, err = rx.transferChunk(ctx, chunk, off, size, done)
|
||||
status := 0
|
||||
if resp != nil {
|
||||
status = resp.StatusCode
|
||||
}
|
||||
// We sent "X-GUploader-No-308: yes" (see comment elsewhere in
|
||||
// this file), so we don't expect to get a 308.
|
||||
if status == 308 {
|
||||
return nil, errors.New("unexpected 308 response status code")
|
||||
}
|
||||
// Chunk upload should be retried if the ChunkTransferTimeout is non-zero and err is context deadline exceeded
|
||||
// or we encounter a retryable error.
|
||||
if (rx.ChunkTransferTimeout != 0 && errors.Is(err, context.DeadlineExceeded)) || shouldRetry(status, err) {
|
||||
rx.attempts++
|
||||
pause = bo.Pause()
|
||||
chunk, _, _, _ = rx.Media.Chunk()
|
||||
continue
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// Upload starts the process of a resumable upload with a cancellable context.
|
||||
// It is called from the auto-generated API code and is not visible to the user.
|
||||
// Before sending an HTTP request, Upload calls any registered hook functions,
|
||||
// and calls the returned functions after the request returns (see send.go).
|
||||
// rx is private to the auto-generated API code.
|
||||
// Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close.
|
||||
// Upload does not parse the response into the error on a non 200 response;
|
||||
// it is the caller's responsibility to call resp.Body.Close.
|
||||
func (rx *ResumableUpload) Upload(ctx context.Context) (*http.Response, error) {
|
||||
for {
|
||||
chunk, off, size, err := rx.Media.Chunk()
|
||||
done := err == io.EOF
|
||||
if !done && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := rx.uploadChunkWithRetries(ctx, chunk, off, int64(size), done)
|
||||
// There are a couple of cases where it's possible for err and resp to both
|
||||
// be non-nil. However, we expose a simpler contract to our callers: exactly
|
||||
// one of resp and err will be non-nil. This means that any response body
|
||||
// must be closed here before returning a non-nil error.
|
||||
if err != nil {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
// If there were retries, indicate this in the error message and wrap the final error.
|
||||
if rx.attempts > 1 {
|
||||
return nil, fmt.Errorf("chunk upload failed after %d attempts, final error: %w", rx.attempts, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This case is very unlikely but possible only if rx.ChunkRetryDeadline is
|
||||
// set to a very small value, in which case no requests will be sent before
|
||||
// the deadline. Return an error to avoid causing a panic.
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("upload request to %v not sent, choose larger value for ChunkRetryDeadline", rx.URI)
|
||||
}
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
rx.reportProgress(off, off+int64(size))
|
||||
}
|
||||
if statusResumeIncomplete(resp) {
|
||||
// The upload is not yet complete, but the server has acknowledged this chunk.
|
||||
// We don't have anything to do with the response body.
|
||||
if resp.Body != nil {
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
rx.Media.Next()
|
||||
continue
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Encode a uint32 as Base64 in big-endian byte order.
|
||||
func encodeUint32(u uint32) string {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, u)
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
123
vendor/google.golang.org/api/internal/gensupport/retry.go
generated
vendored
Normal file
123
vendor/google.golang.org/api/internal/gensupport/retry.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2021 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
// Backoff is an interface around gax.Backoff's Pause method, allowing tests to provide their
|
||||
// own implementation.
|
||||
type Backoff interface {
|
||||
Pause() time.Duration
|
||||
}
|
||||
|
||||
// These are declared as global variables so that tests can overwrite them.
|
||||
var (
|
||||
// Default per-chunk deadline for resumable uploads.
|
||||
defaultRetryDeadline = 32 * time.Second
|
||||
// Default backoff timer.
|
||||
backoff = func() Backoff {
|
||||
return &gax.Backoff{Initial: 100 * time.Millisecond}
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// statusTooManyRequests is returned by the storage API if the
|
||||
// per-project limits have been temporarily exceeded. The request
|
||||
// should be retried.
|
||||
// https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes
|
||||
statusTooManyRequests = 429
|
||||
|
||||
// statusRequestTimeout is returned by the storage API if the
|
||||
// upload connection was broken. The request should be retried.
|
||||
statusRequestTimeout = 408
|
||||
)
|
||||
|
||||
// shouldRetry indicates whether an error is retryable for the purposes of this
|
||||
// package, unless a ShouldRetry func is specified by the RetryConfig instead.
|
||||
// It follows guidance from
|
||||
// https://cloud.google.com/storage/docs/exponential-backoff .
|
||||
func shouldRetry(status int, err error) bool {
|
||||
if 500 <= status && status <= 599 {
|
||||
return true
|
||||
}
|
||||
if status == statusTooManyRequests || status == statusRequestTimeout {
|
||||
return true
|
||||
}
|
||||
if errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return true
|
||||
}
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return true
|
||||
}
|
||||
switch e := err.(type) {
|
||||
case *net.OpError, *url.Error:
|
||||
// Retry socket-level errors ECONNREFUSED and ECONNRESET (from syscall).
|
||||
// Unfortunately the error type is unexported, so we resort to string
|
||||
// matching.
|
||||
retriable := []string{"connection refused", "connection reset", "broken pipe"}
|
||||
for _, s := range retriable {
|
||||
if strings.Contains(e.Error(), s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case interface{ Temporary() bool }:
|
||||
if e.Temporary() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// If error unwrapping is available, use this to examine wrapped
|
||||
// errors.
|
||||
if e, ok := err.(interface{ Unwrap() error }); ok {
|
||||
return shouldRetry(status, e.Unwrap())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RetryConfig allows configuration of backoff timing and retryable errors.
|
||||
type RetryConfig struct {
|
||||
Backoff *gax.Backoff
|
||||
ShouldRetry func(err error) bool
|
||||
}
|
||||
|
||||
// Get a new backoff object based on the configured values.
|
||||
func (r *RetryConfig) backoff() Backoff {
|
||||
if r == nil || r.Backoff == nil {
|
||||
return backoff()
|
||||
}
|
||||
return &gax.Backoff{
|
||||
Initial: r.Backoff.Initial,
|
||||
Max: r.Backoff.Max,
|
||||
Multiplier: r.Backoff.Multiplier,
|
||||
}
|
||||
}
|
||||
|
||||
// This is kind of hacky; it is necessary because ShouldRetry expects to
|
||||
// handle HTTP errors via googleapi.Error, but the error has not yet been
|
||||
// wrapped with a googleapi.Error at this layer, and the ErrorFunc type
|
||||
// in the manual layer does not pass in a status explicitly as it does
|
||||
// here. So, we must wrap error status codes in a googleapi.Error so that
|
||||
// ShouldRetry can parse this correctly.
|
||||
func (r *RetryConfig) errorFunc() func(status int, err error) bool {
|
||||
if r == nil || r.ShouldRetry == nil {
|
||||
return shouldRetry
|
||||
}
|
||||
return func(status int, err error) bool {
|
||||
if status >= 400 {
|
||||
return r.ShouldRetry(&googleapi.Error{Code: status})
|
||||
}
|
||||
return r.ShouldRetry(err)
|
||||
}
|
||||
}
|
||||
241
vendor/google.golang.org/api/internal/gensupport/send.go
generated
vendored
Normal file
241
vendor/google.golang.org/api/internal/gensupport/send.go
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
"github.com/googleapis/gax-go/v2/callctx"
|
||||
)
|
||||
|
||||
// Use this error type to return an error which allows introspection of both
|
||||
// the context error and the error from the service.
|
||||
type wrappedCallErr struct {
|
||||
ctxErr error
|
||||
wrappedErr error
|
||||
}
|
||||
|
||||
func (e wrappedCallErr) Error() string {
|
||||
return fmt.Sprintf("retry failed with %v; last error: %v", e.ctxErr, e.wrappedErr)
|
||||
}
|
||||
|
||||
func (e wrappedCallErr) Unwrap() error {
|
||||
return e.wrappedErr
|
||||
}
|
||||
|
||||
// Is allows errors.Is to match the error from the call as well as context
|
||||
// sentinel errors.
|
||||
func (e wrappedCallErr) Is(target error) bool {
|
||||
return errors.Is(e.ctxErr, target) || errors.Is(e.wrappedErr, target)
|
||||
}
|
||||
|
||||
// SendRequest sends a single HTTP request using the given client.
|
||||
// If ctx is non-nil, it calls all hooks, then sends the request with
|
||||
// req.WithContext, then calls any functions returned by the hooks in
|
||||
// reverse order.
|
||||
func SendRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||
// Add headers set in context metadata.
|
||||
if ctx != nil {
|
||||
headers := callctx.HeadersFromContext(ctx)
|
||||
for k, vals := range headers {
|
||||
if k == "x-goog-api-client" {
|
||||
// Merge all values into a single "x-goog-api-client" header.
|
||||
var mergedVal strings.Builder
|
||||
baseXGoogHeader := req.Header.Get("X-Goog-Api-Client")
|
||||
if baseXGoogHeader != "" {
|
||||
mergedVal.WriteString(baseXGoogHeader)
|
||||
mergedVal.WriteRune(' ')
|
||||
}
|
||||
for _, v := range vals {
|
||||
mergedVal.WriteString(v)
|
||||
mergedVal.WriteRune(' ')
|
||||
}
|
||||
// Remove the last space and replace the header on the request.
|
||||
req.Header.Set(k, mergedVal.String()[:mergedVal.Len()-1])
|
||||
} else {
|
||||
for _, v := range vals {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow Accept-Encoding because it interferes with the automatic gzip handling
|
||||
// done by the default http.Transport. See https://github.com/google/google-api-go-client/issues/219.
|
||||
if _, ok := req.Header["Accept-Encoding"]; ok {
|
||||
return nil, errors.New("google api: custom Accept-Encoding headers not allowed")
|
||||
}
|
||||
if ctx == nil {
|
||||
return client.Do(req)
|
||||
}
|
||||
return send(ctx, client, req)
|
||||
}
|
||||
|
||||
func send(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
// If we got an error, and the context has been canceled,
|
||||
// the context's error is probably more useful.
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// SendRequestWithRetry sends a single HTTP request using the given client,
|
||||
// with retries if a retryable error is returned.
|
||||
// If ctx is non-nil, it calls all hooks, then sends the request with
|
||||
// req.WithContext, then calls any functions returned by the hooks in
|
||||
// reverse order.
|
||||
func SendRequestWithRetry(ctx context.Context, client *http.Client, req *http.Request, retry *RetryConfig) (*http.Response, error) {
|
||||
// Add headers set in context metadata.
|
||||
if ctx != nil {
|
||||
headers := callctx.HeadersFromContext(ctx)
|
||||
for k, vals := range headers {
|
||||
for _, v := range vals {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow Accept-Encoding because it interferes with the automatic gzip handling
|
||||
// done by the default http.Transport. See https://github.com/google/google-api-go-client/issues/219.
|
||||
if _, ok := req.Header["Accept-Encoding"]; ok {
|
||||
return nil, errors.New("google api: custom Accept-Encoding headers not allowed")
|
||||
}
|
||||
if ctx == nil {
|
||||
return client.Do(req)
|
||||
}
|
||||
return sendAndRetry(ctx, client, req, retry)
|
||||
}
|
||||
|
||||
func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request, retry *RetryConfig) (*http.Response, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
var err error
|
||||
attempts := 1
|
||||
invocationID := uuid.New().String()
|
||||
|
||||
xGoogHeaderVals := req.Header.Values("X-Goog-Api-Client")
|
||||
baseXGoogHeader := strings.Join(xGoogHeaderVals, " ")
|
||||
|
||||
// Loop to retry the request, up to the context deadline.
|
||||
var pause time.Duration
|
||||
var bo Backoff
|
||||
if retry != nil && retry.Backoff != nil {
|
||||
bo = &gax.Backoff{
|
||||
Initial: retry.Backoff.Initial,
|
||||
Max: retry.Backoff.Max,
|
||||
Multiplier: retry.Backoff.Multiplier,
|
||||
}
|
||||
} else {
|
||||
bo = backoff()
|
||||
}
|
||||
|
||||
var errorFunc = retry.errorFunc()
|
||||
|
||||
for {
|
||||
t := time.NewTimer(pause)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
// If we got an error and the context has been canceled, return an error acknowledging
|
||||
// both the context cancelation and the service error.
|
||||
if err != nil {
|
||||
return resp, wrappedCallErr{ctx.Err(), err}
|
||||
}
|
||||
return resp, ctx.Err()
|
||||
case <-t.C:
|
||||
}
|
||||
|
||||
if ctx.Err() != nil {
|
||||
// Check for context cancellation once more. If more than one case in a
|
||||
// select is satisfied at the same time, Go will choose one arbitrarily.
|
||||
// That can cause an operation to go through even if the context was
|
||||
// canceled before.
|
||||
if err != nil {
|
||||
return resp, wrappedCallErr{ctx.Err(), err}
|
||||
}
|
||||
return resp, ctx.Err()
|
||||
}
|
||||
|
||||
// Set retry metrics and idempotency headers for GCS.
|
||||
// TODO(b/274504690): Consider dropping gccl-invocation-id key since it
|
||||
// duplicates the X-Goog-Gcs-Idempotency-Token header (added in v0.115.0).
|
||||
invocationHeader := fmt.Sprintf("gccl-invocation-id/%s gccl-attempt-count/%d", invocationID, attempts)
|
||||
xGoogHeader := strings.Join([]string{invocationHeader, baseXGoogHeader}, " ")
|
||||
req.Header.Set("X-Goog-Api-Client", xGoogHeader)
|
||||
req.Header.Set("X-Goog-Gcs-Idempotency-Token", invocationID)
|
||||
|
||||
resp, err = client.Do(req.WithContext(ctx))
|
||||
|
||||
var status int
|
||||
if resp != nil {
|
||||
status = resp.StatusCode
|
||||
}
|
||||
|
||||
// Check if we can retry the request. A retry can only be done if the error
|
||||
// is retryable and the request body can be re-created using GetBody (this
|
||||
// will not be possible if the body was unbuffered).
|
||||
if req.GetBody == nil || !errorFunc(status, err) {
|
||||
break
|
||||
}
|
||||
attempts++
|
||||
var errBody error
|
||||
req.Body, errBody = req.GetBody()
|
||||
if errBody != nil {
|
||||
break
|
||||
}
|
||||
|
||||
pause = bo.Pause()
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DecodeResponse decodes the body of res into target. If there is no body,
|
||||
// target is unchanged.
|
||||
func DecodeResponse(target interface{}, res *http.Response) error {
|
||||
if res.StatusCode == http.StatusNoContent {
|
||||
return nil
|
||||
}
|
||||
return json.NewDecoder(res.Body).Decode(target)
|
||||
}
|
||||
|
||||
// DecodeResponseBytes decodes the body of res into target and returns bytes read
|
||||
// from the body. If there is no body, target is unchanged.
|
||||
func DecodeResponseBytes(target interface{}, res *http.Response) ([]byte, error) {
|
||||
if res.StatusCode == http.StatusNoContent {
|
||||
return nil, nil
|
||||
}
|
||||
b, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(b, target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
53
vendor/google.golang.org/api/internal/gensupport/version.go
generated
vendored
Normal file
53
vendor/google.golang.org/api/internal/gensupport/version.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2020 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gensupport
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// GoVersion returns the Go runtime version. The returned string
|
||||
// has no whitespace.
|
||||
func GoVersion() string {
|
||||
return goVersion
|
||||
}
|
||||
|
||||
var goVersion = goVer(runtime.Version())
|
||||
|
||||
const develPrefix = "devel +"
|
||||
|
||||
func goVer(s string) string {
|
||||
if strings.HasPrefix(s, develPrefix) {
|
||||
s = s[len(develPrefix):]
|
||||
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
||||
s = s[:p]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, "go1") {
|
||||
s = s[2:]
|
||||
var prerelease string
|
||||
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
|
||||
s, prerelease = s[:p], s[p:]
|
||||
}
|
||||
if strings.HasSuffix(s, ".") {
|
||||
s += "0"
|
||||
} else if strings.Count(s, ".") < 2 {
|
||||
s += ".0"
|
||||
}
|
||||
if prerelease != "" {
|
||||
s += "-" + prerelease
|
||||
}
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func notSemverRune(r rune) bool {
|
||||
return !strings.ContainsRune("0123456789.", r)
|
||||
}
|
||||
127
vendor/google.golang.org/api/internal/impersonate/impersonate.go
generated
vendored
Normal file
127
vendor/google.golang.org/api/internal/impersonate/impersonate.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package impersonate is used to impersonate Google Credentials.
|
||||
package impersonate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// Config for generating impersonated credentials.
|
||||
type Config struct {
|
||||
// Target is the service account to impersonate. Required.
|
||||
Target string
|
||||
// Scopes the impersonated credential should have. Required.
|
||||
Scopes []string
|
||||
// Delegates are the service accounts in a delegation chain. Each service
|
||||
// account must be granted roles/iam.serviceAccountTokenCreator on the next
|
||||
// service account in the chain. Optional.
|
||||
Delegates []string
|
||||
}
|
||||
|
||||
// TokenSource returns an impersonated TokenSource configured with the provided
|
||||
// config using ts as the base credential provider for making requests.
|
||||
func TokenSource(ctx context.Context, ts oauth2.TokenSource, config *Config) (oauth2.TokenSource, error) {
|
||||
if len(config.Scopes) == 0 {
|
||||
return nil, fmt.Errorf("impersonate: scopes must be provided")
|
||||
}
|
||||
its := impersonatedTokenSource{
|
||||
ctx: ctx,
|
||||
ts: ts,
|
||||
name: formatIAMServiceAccountName(config.Target),
|
||||
// Default to the longest acceptable value of one hour as the token will
|
||||
// be refreshed automatically.
|
||||
lifetime: "3600s",
|
||||
}
|
||||
|
||||
its.delegates = make([]string, len(config.Delegates))
|
||||
for i, v := range config.Delegates {
|
||||
its.delegates[i] = formatIAMServiceAccountName(v)
|
||||
}
|
||||
its.scopes = make([]string, len(config.Scopes))
|
||||
copy(its.scopes, config.Scopes)
|
||||
|
||||
return oauth2.ReuseTokenSource(nil, its), nil
|
||||
}
|
||||
|
||||
func formatIAMServiceAccountName(name string) string {
|
||||
return fmt.Sprintf("projects/-/serviceAccounts/%s", name)
|
||||
}
|
||||
|
||||
type generateAccessTokenReq struct {
|
||||
Delegates []string `json:"delegates,omitempty"`
|
||||
Lifetime string `json:"lifetime,omitempty"`
|
||||
Scope []string `json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
type generateAccessTokenResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
ExpireTime string `json:"expireTime"`
|
||||
}
|
||||
|
||||
type impersonatedTokenSource struct {
|
||||
ctx context.Context
|
||||
ts oauth2.TokenSource
|
||||
|
||||
name string
|
||||
lifetime string
|
||||
scopes []string
|
||||
delegates []string
|
||||
}
|
||||
|
||||
// Token returns an impersonated Token.
|
||||
func (i impersonatedTokenSource) Token() (*oauth2.Token, error) {
|
||||
hc := oauth2.NewClient(i.ctx, i.ts)
|
||||
reqBody := generateAccessTokenReq{
|
||||
Delegates: i.delegates,
|
||||
Lifetime: i.lifetime,
|
||||
Scope: i.scopes,
|
||||
}
|
||||
b, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to marshal request: %v", err)
|
||||
}
|
||||
url := fmt.Sprintf("https://iamcredentials.googleapis.com/v1/%s:generateAccessToken", i.name)
|
||||
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to create request: %v", err)
|
||||
}
|
||||
req = req.WithContext(i.ctx)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to generate access token: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to read body: %v", err)
|
||||
}
|
||||
if c := resp.StatusCode; c < 200 || c > 299 {
|
||||
return nil, fmt.Errorf("impersonate: status code %d: %s", c, body)
|
||||
}
|
||||
|
||||
var accessTokenResp generateAccessTokenResp
|
||||
if err := json.Unmarshal(body, &accessTokenResp); err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to parse response: %v", err)
|
||||
}
|
||||
expiry, err := time.Parse(time.RFC3339, accessTokenResp.ExpireTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to parse expiry: %v", err)
|
||||
}
|
||||
return &oauth2.Token{
|
||||
AccessToken: accessTokenResp.AccessToken,
|
||||
Expiry: expiry,
|
||||
}, nil
|
||||
}
|
||||
136
vendor/google.golang.org/api/internal/s2a.go
generated
vendored
Normal file
136
vendor/google.golang.org/api/internal/s2a.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2023 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
)
|
||||
|
||||
const configEndpointSuffix = "instance/platform-security/auto-mtls-configuration"
|
||||
|
||||
// The period an MTLS config can be reused before needing refresh.
|
||||
var configExpiry = time.Hour
|
||||
|
||||
// GetS2AAddress returns the S2A address to be reached via plaintext connection.
|
||||
func GetS2AAddress() string {
|
||||
c, err := getMetadataMTLSAutoConfig().Config()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if !c.Valid() {
|
||||
return ""
|
||||
}
|
||||
return c.S2A.PlaintextAddress
|
||||
}
|
||||
|
||||
type mtlsConfigSource interface {
|
||||
Config() (*mtlsConfig, error)
|
||||
}
|
||||
|
||||
// mdsMTLSAutoConfigSource is an instance of reuseMTLSConfigSource, with metadataMTLSAutoConfig as its config source.
|
||||
var (
|
||||
mdsMTLSAutoConfigSource mtlsConfigSource
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// getMetadataMTLSAutoConfig returns mdsMTLSAutoConfigSource, which is backed by config from MDS with auto-refresh.
|
||||
func getMetadataMTLSAutoConfig() mtlsConfigSource {
|
||||
once.Do(func() {
|
||||
mdsMTLSAutoConfigSource = &reuseMTLSConfigSource{
|
||||
src: &metadataMTLSAutoConfig{},
|
||||
}
|
||||
})
|
||||
return mdsMTLSAutoConfigSource
|
||||
}
|
||||
|
||||
// reuseMTLSConfigSource caches a valid version of mtlsConfig, and uses `src` to refresh upon config expiry.
|
||||
// It implements the mtlsConfigSource interface, so calling Config() on it returns an mtlsConfig.
|
||||
type reuseMTLSConfigSource struct {
|
||||
src mtlsConfigSource // src.Config() is called when config is expired
|
||||
mu sync.Mutex // mutex guards config
|
||||
config *mtlsConfig // cached config
|
||||
}
|
||||
|
||||
func (cs *reuseMTLSConfigSource) Config() (*mtlsConfig, error) {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
|
||||
if cs.config.Valid() {
|
||||
return cs.config, nil
|
||||
}
|
||||
c, err := cs.src.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.config = c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// metadataMTLSAutoConfig is an implementation of the interface mtlsConfigSource
|
||||
// It has the logic to query MDS and return an mtlsConfig
|
||||
type metadataMTLSAutoConfig struct{}
|
||||
|
||||
var httpGetMetadataMTLSConfig = func() (string, error) {
|
||||
return metadata.Get(configEndpointSuffix)
|
||||
}
|
||||
|
||||
func (cs *metadataMTLSAutoConfig) Config() (*mtlsConfig, error) {
|
||||
resp, err := httpGetMetadataMTLSConfig()
|
||||
if err != nil {
|
||||
log.Printf("querying MTLS config from MDS endpoint failed: %v", err)
|
||||
return defaultMTLSConfig(), nil
|
||||
}
|
||||
var config mtlsConfig
|
||||
err = json.Unmarshal([]byte(resp), &config)
|
||||
if err != nil {
|
||||
log.Printf("unmarshalling MTLS config from MDS endpoint failed: %v", err)
|
||||
return defaultMTLSConfig(), nil
|
||||
}
|
||||
|
||||
if config.S2A == nil {
|
||||
log.Printf("returned MTLS config from MDS endpoint is invalid: %v", config)
|
||||
return defaultMTLSConfig(), nil
|
||||
}
|
||||
|
||||
// set new expiry
|
||||
config.Expiry = time.Now().Add(configExpiry)
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func defaultMTLSConfig() *mtlsConfig {
|
||||
return &mtlsConfig{
|
||||
S2A: &s2aAddresses{
|
||||
PlaintextAddress: "",
|
||||
MTLSAddress: "",
|
||||
},
|
||||
Expiry: time.Now().Add(configExpiry),
|
||||
}
|
||||
}
|
||||
|
||||
// s2aAddresses contains the plaintext and/or MTLS S2A addresses.
|
||||
type s2aAddresses struct {
|
||||
// PlaintextAddress is the plaintext address to reach S2A
|
||||
PlaintextAddress string `json:"plaintext_address"`
|
||||
// MTLSAddress is the MTLS address to reach S2A
|
||||
MTLSAddress string `json:"mtls_address"`
|
||||
}
|
||||
|
||||
// mtlsConfig contains the configuration for establishing MTLS connections with Google APIs.
|
||||
type mtlsConfig struct {
|
||||
S2A *s2aAddresses `json:"s2a"`
|
||||
Expiry time.Time
|
||||
}
|
||||
|
||||
func (c *mtlsConfig) Valid() bool {
|
||||
return c != nil && c.S2A != nil && !c.expired()
|
||||
}
|
||||
func (c *mtlsConfig) expired() bool {
|
||||
return c.Expiry.Before(time.Now())
|
||||
}
|
||||
303
vendor/google.golang.org/api/internal/settings.go
generated
vendored
Normal file
303
vendor/google.golang.org/api/internal/settings.go
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
// Copyright 2017 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package internal supports the options and transport packages.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/auth"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/internal/credentialstype"
|
||||
"google.golang.org/api/internal/impersonate"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
newAuthLibEnvVar = "GOOGLE_API_GO_EXPERIMENTAL_ENABLE_NEW_AUTH_LIB"
|
||||
newAuthLibDisabledEnVar = "GOOGLE_API_GO_EXPERIMENTAL_DISABLE_NEW_AUTH_LIB"
|
||||
universeDomainEnvVar = "GOOGLE_CLOUD_UNIVERSE_DOMAIN"
|
||||
defaultUniverseDomain = "googleapis.com"
|
||||
)
|
||||
|
||||
// DialSettings holds information needed to establish a connection with a
|
||||
// Google API service.
|
||||
type DialSettings struct {
|
||||
Endpoint string
|
||||
DefaultEndpoint string
|
||||
DefaultEndpointTemplate string
|
||||
DefaultMTLSEndpoint string
|
||||
Scopes []string
|
||||
DefaultScopes []string
|
||||
EnableJwtWithScope bool
|
||||
TokenSource oauth2.TokenSource
|
||||
Credentials *google.Credentials
|
||||
// Deprecated: Use AuthCredentialsFile instead, due to security risk.
|
||||
CredentialsFile string
|
||||
// Deprecated: Use AuthCredentialsJSON instead, due to security risk.
|
||||
CredentialsJSON []byte
|
||||
InternalCredentials *google.Credentials
|
||||
UserAgent string
|
||||
APIKey string
|
||||
Audiences []string
|
||||
DefaultAudience string
|
||||
HTTPClient *http.Client
|
||||
GRPCDialOpts []grpc.DialOption
|
||||
GRPCConn *grpc.ClientConn
|
||||
GRPCConnPool ConnPool
|
||||
GRPCConnPoolSize int
|
||||
NoAuth bool
|
||||
TelemetryDisabled bool
|
||||
ClientCertSource func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||
CustomClaims map[string]interface{}
|
||||
SkipValidation bool
|
||||
ImpersonationConfig *impersonate.Config
|
||||
EnableDirectPath bool
|
||||
EnableDirectPathXds bool
|
||||
AllowNonDefaultServiceAccount bool
|
||||
DefaultUniverseDomain string
|
||||
UniverseDomain string
|
||||
AllowHardBoundTokens []string
|
||||
Logger *slog.Logger
|
||||
// Google API system parameters. For more information please read:
|
||||
// https://cloud.google.com/apis/docs/system-parameters
|
||||
QuotaProject string
|
||||
RequestReason string
|
||||
|
||||
// TelemetryAttributes specifies a map of telemetry attributes to be added
|
||||
// to all OpenTelemetry signals, such as tracing and metrics, for purposes
|
||||
// including representing the static identity of the client (e.g., service
|
||||
// name, version). These attributes are expected to be consistent across all
|
||||
// signals to enable cross-signal correlation.
|
||||
TelemetryAttributes map[string]string
|
||||
|
||||
// New Auth library Options
|
||||
AuthCredentials *auth.Credentials
|
||||
AuthCredentialsJSON []byte
|
||||
AuthCredentialsFile string
|
||||
AuthCredentialsType credentialstype.CredType
|
||||
EnableNewAuthLibrary bool
|
||||
|
||||
// TODO(b/372244283): Remove after b/358175516 has been fixed
|
||||
EnableAsyncRefreshDryRun func()
|
||||
}
|
||||
|
||||
// GetScopes returns the user-provided scopes, if set, or else falls back to the
|
||||
// default scopes.
|
||||
func (ds *DialSettings) GetScopes() []string {
|
||||
if len(ds.Scopes) > 0 {
|
||||
return ds.Scopes
|
||||
}
|
||||
return ds.DefaultScopes
|
||||
}
|
||||
|
||||
// GetAudience returns the user-provided audience, if set, or else falls back to the default audience.
|
||||
func (ds *DialSettings) GetAudience() string {
|
||||
if ds.HasCustomAudience() {
|
||||
return ds.Audiences[0]
|
||||
}
|
||||
return ds.DefaultAudience
|
||||
}
|
||||
|
||||
// HasCustomAudience returns true if a custom audience is provided by users.
|
||||
func (ds *DialSettings) HasCustomAudience() bool {
|
||||
return len(ds.Audiences) > 0
|
||||
}
|
||||
|
||||
// IsNewAuthLibraryEnabled returns true if the new auth library should be used.
|
||||
func (ds *DialSettings) IsNewAuthLibraryEnabled() bool {
|
||||
// Disabled env is for future rollouts to make sure there is a way to easily
|
||||
// disable this behaviour once we switch in on by default.
|
||||
if b, err := strconv.ParseBool(os.Getenv(newAuthLibDisabledEnVar)); err == nil && b {
|
||||
return false
|
||||
}
|
||||
if ds.EnableNewAuthLibrary {
|
||||
return true
|
||||
}
|
||||
if ds.AuthCredentials != nil {
|
||||
return true
|
||||
}
|
||||
if len(ds.AuthCredentialsJSON) > 0 {
|
||||
return true
|
||||
}
|
||||
if ds.AuthCredentialsFile != "" {
|
||||
return true
|
||||
}
|
||||
if b, err := strconv.ParseBool(os.Getenv(newAuthLibEnvVar)); err == nil {
|
||||
return b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAuthCredentialsJSON returns the AuthCredentialsJSON and AuthCredentialsType, if set.
|
||||
// Otherwise it falls back to the deprecated CredentialsJSON with an Unknown type.
|
||||
//
|
||||
// Use AuthCredentialsJSON if provided, as it is the safer, recommended option.
|
||||
// CredentialsJSON is populated by the deprecated WithCredentialsJSON.
|
||||
func (ds *DialSettings) GetAuthCredentialsJSON() ([]byte, credentialstype.CredType) {
|
||||
if len(ds.AuthCredentialsJSON) > 0 {
|
||||
return ds.AuthCredentialsJSON, ds.AuthCredentialsType
|
||||
}
|
||||
return ds.CredentialsJSON, credentialstype.Unknown
|
||||
}
|
||||
|
||||
// GetAuthCredentialsFile returns the AuthCredentialsFile and AuthCredentialsType, if set.
|
||||
// Otherwise it falls back to the deprecated CredentialsFile with an Unknown type.
|
||||
//
|
||||
// Use AuthCredentialsFile if provided, as it is the safer, recommended option.
|
||||
// CredentialsFile is populated by the deprecated WithCredentialsFile.
|
||||
func (ds *DialSettings) GetAuthCredentialsFile() (string, credentialstype.CredType) {
|
||||
if ds.AuthCredentialsFile != "" {
|
||||
return ds.AuthCredentialsFile, ds.AuthCredentialsType
|
||||
}
|
||||
return ds.CredentialsFile, credentialstype.Unknown
|
||||
}
|
||||
|
||||
// Validate reports an error if ds is invalid.
|
||||
func (ds *DialSettings) Validate() error {
|
||||
if ds.SkipValidation {
|
||||
return nil
|
||||
}
|
||||
hasCreds := ds.APIKey != "" || ds.TokenSource != nil || ds.CredentialsFile != "" || ds.Credentials != nil || ds.AuthCredentials != nil || len(ds.AuthCredentialsJSON) > 0 || ds.AuthCredentialsFile != ""
|
||||
if ds.NoAuth && hasCreds {
|
||||
return errors.New("options.WithoutAuthentication is incompatible with any option that provides credentials")
|
||||
}
|
||||
// Credentials should not appear with other options.
|
||||
// AuthCredentials is a special case that may be present with
|
||||
// with other options in order to facilitate automatic conversion of
|
||||
// oauth2 types (old auth) to cloud.google.com/go/auth types (new auth).
|
||||
// We currently allow TokenSource and CredentialsFile to coexist.
|
||||
// TODO(jba): make TokenSource & CredentialsFile an error (breaking change).
|
||||
nCreds := 0
|
||||
if ds.Credentials != nil {
|
||||
nCreds++
|
||||
}
|
||||
if len(ds.CredentialsJSON) > 0 {
|
||||
nCreds++
|
||||
}
|
||||
if len(ds.AuthCredentialsJSON) > 0 {
|
||||
nCreds++
|
||||
}
|
||||
if ds.AuthCredentialsFile != "" {
|
||||
nCreds++
|
||||
}
|
||||
if ds.CredentialsFile != "" {
|
||||
nCreds++
|
||||
}
|
||||
if ds.APIKey != "" {
|
||||
nCreds++
|
||||
}
|
||||
if ds.TokenSource != nil {
|
||||
nCreds++
|
||||
}
|
||||
if len(ds.Scopes) > 0 && len(ds.Audiences) > 0 {
|
||||
return errors.New("WithScopes is incompatible with WithAudience")
|
||||
}
|
||||
// Accept only one form of credentials, except we allow TokenSource and CredentialsFile for backwards compatibility.
|
||||
if nCreds > 1 && !(nCreds == 2 && ds.TokenSource != nil && ds.CredentialsFile != "") {
|
||||
return errors.New("multiple credential options provided")
|
||||
}
|
||||
if ds.GRPCConn != nil && ds.GRPCConnPool != nil {
|
||||
return errors.New("WithGRPCConn is incompatible with WithConnPool")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.GRPCConnPool != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with WithConnPool")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.GRPCConn != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with WithGRPCConn")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.GRPCDialOpts != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with gRPC dial options")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.QuotaProject != "" {
|
||||
return errors.New("WithHTTPClient is incompatible with QuotaProject")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.RequestReason != "" {
|
||||
return errors.New("WithHTTPClient is incompatible with RequestReason")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.ClientCertSource != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with WithClientCertSource")
|
||||
}
|
||||
if ds.ClientCertSource != nil && (ds.GRPCConn != nil || ds.GRPCConnPool != nil || ds.GRPCConnPoolSize != 0 || ds.GRPCDialOpts != nil) {
|
||||
return errors.New("WithClientCertSource is currently only supported for HTTP. gRPC settings are incompatible")
|
||||
}
|
||||
if ds.ImpersonationConfig != nil && len(ds.ImpersonationConfig.Scopes) == 0 && len(ds.Scopes) == 0 {
|
||||
return errors.New("WithImpersonatedCredentials requires scopes being provided")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultUniverseDomain returns the Google default universe domain
|
||||
// ("googleapis.com").
|
||||
func (ds *DialSettings) GetDefaultUniverseDomain() string {
|
||||
return defaultUniverseDomain
|
||||
}
|
||||
|
||||
// GetUniverseDomain returns the default service domain for a given Cloud
|
||||
// universe, with the following precedence:
|
||||
//
|
||||
// 1. A non-empty option.WithUniverseDomain.
|
||||
// 2. A non-empty environment variable GOOGLE_CLOUD_UNIVERSE_DOMAIN.
|
||||
// 3. The default value "googleapis.com".
|
||||
func (ds *DialSettings) GetUniverseDomain() string {
|
||||
if ds.UniverseDomain != "" {
|
||||
return ds.UniverseDomain
|
||||
}
|
||||
if envUD := os.Getenv(universeDomainEnvVar); envUD != "" {
|
||||
return envUD
|
||||
}
|
||||
return defaultUniverseDomain
|
||||
}
|
||||
|
||||
// IsUniverseDomainGDU returns true if the universe domain is the default Google
|
||||
// universe ("googleapis.com").
|
||||
func (ds *DialSettings) IsUniverseDomainGDU() bool {
|
||||
return ds.GetUniverseDomain() == defaultUniverseDomain
|
||||
}
|
||||
|
||||
// GetUniverseDomain returns the default service domain for a given Cloud
|
||||
// universe, from google.Credentials. This wrapper function should be removed
|
||||
// to close https://github.com/googleapis/google-api-go-client/issues/2399.
|
||||
func GetUniverseDomain(creds *google.Credentials) (string, error) {
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
errors := make(chan error)
|
||||
results := make(chan string)
|
||||
|
||||
go func() {
|
||||
result, err := creds.GetUniverseDomain()
|
||||
if err != nil {
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
results <- result
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errors:
|
||||
// An error that is returned before the timer expires is likely to be
|
||||
// connection refused. Temporarily (2024-03-21) return the GDU domain.
|
||||
return defaultUniverseDomain, nil
|
||||
case res := <-results:
|
||||
return res, nil
|
||||
case <-timer.C: // Timer is expired.
|
||||
// If err or res was not returned, it means that creds.GetUniverseDomain()
|
||||
// did not complete in 1s. Assume that MDS is likely never responding to
|
||||
// the endpoint and will timeout. This is the source of issues such as
|
||||
// https://github.com/googleapis/google-cloud-go/issues/9350.
|
||||
// Temporarily (2024-02-02) return the GDU domain. Restore the original
|
||||
// calls to creds.GetUniverseDomain() in grpc/dial.go and http/dial.go
|
||||
// and remove this method to close
|
||||
// https://github.com/googleapis/google-api-go-client/issues/2399.
|
||||
return defaultUniverseDomain, nil
|
||||
}
|
||||
}
|
||||
27
vendor/google.golang.org/api/internal/third_party/uritemplates/LICENSE
generated
vendored
Normal file
27
vendor/google.golang.org/api/internal/third_party/uritemplates/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2013 Joshua Tacoma. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
14
vendor/google.golang.org/api/internal/third_party/uritemplates/METADATA
generated
vendored
Normal file
14
vendor/google.golang.org/api/internal/third_party/uritemplates/METADATA
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: "uritemplates"
|
||||
description:
|
||||
"Package uritemplates is a level 4 implementation of RFC 6570 (URI "
|
||||
"Template, http://tools.ietf.org/html/rfc6570)."
|
||||
|
||||
third_party {
|
||||
url {
|
||||
type: GIT
|
||||
value: "https://github.com/jtacoma/uritemplates"
|
||||
}
|
||||
version: "0.1"
|
||||
last_upgrade_date { year: 2014 month: 8 day: 18 }
|
||||
license_type: NOTICE
|
||||
}
|
||||
248
vendor/google.golang.org/api/internal/third_party/uritemplates/uritemplates.go
generated
vendored
Normal file
248
vendor/google.golang.org/api/internal/third_party/uritemplates/uritemplates.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2013 Joshua Tacoma. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uritemplates is a level 3 implementation of RFC 6570 (URI
|
||||
// Template, http://tools.ietf.org/html/rfc6570).
|
||||
// uritemplates does not support composite values (in Go: slices or maps)
|
||||
// and so does not qualify as a level 4 implementation.
|
||||
package uritemplates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
|
||||
reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
|
||||
validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
|
||||
hex = []byte("0123456789ABCDEF")
|
||||
)
|
||||
|
||||
func pctEncode(src []byte) []byte {
|
||||
dst := make([]byte, len(src)*3)
|
||||
for i, b := range src {
|
||||
buf := dst[i*3 : i*3+3]
|
||||
buf[0] = 0x25
|
||||
buf[1] = hex[b/16]
|
||||
buf[2] = hex[b%16]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// pairWriter is a convenience struct which allows escaped and unescaped
|
||||
// versions of the template to be written in parallel.
|
||||
type pairWriter struct {
|
||||
escaped, unescaped bytes.Buffer
|
||||
}
|
||||
|
||||
// Write writes the provided string directly without any escaping.
|
||||
func (w *pairWriter) Write(s string) {
|
||||
w.escaped.WriteString(s)
|
||||
w.unescaped.WriteString(s)
|
||||
}
|
||||
|
||||
// Escape writes the provided string, escaping the string for the
|
||||
// escaped output.
|
||||
func (w *pairWriter) Escape(s string, allowReserved bool) {
|
||||
w.unescaped.WriteString(s)
|
||||
if allowReserved {
|
||||
w.escaped.Write(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
} else {
|
||||
w.escaped.Write(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
}
|
||||
|
||||
// Escaped returns the escaped string.
|
||||
func (w *pairWriter) Escaped() string {
|
||||
return w.escaped.String()
|
||||
}
|
||||
|
||||
// Unescaped returns the unescaped string.
|
||||
func (w *pairWriter) Unescaped() string {
|
||||
return w.unescaped.String()
|
||||
}
|
||||
|
||||
// A uriTemplate is a parsed representation of a URI template.
|
||||
type uriTemplate struct {
|
||||
raw string
|
||||
parts []templatePart
|
||||
}
|
||||
|
||||
// parse parses a URI template string into a uriTemplate object.
|
||||
func parse(rawTemplate string) (*uriTemplate, error) {
|
||||
split := strings.Split(rawTemplate, "{")
|
||||
parts := make([]templatePart, len(split)*2-1)
|
||||
for i, s := range split {
|
||||
if i == 0 {
|
||||
if strings.Contains(s, "}") {
|
||||
return nil, errors.New("unexpected }")
|
||||
}
|
||||
parts[i].raw = s
|
||||
continue
|
||||
}
|
||||
subsplit := strings.Split(s, "}")
|
||||
if len(subsplit) != 2 {
|
||||
return nil, errors.New("malformed template")
|
||||
}
|
||||
expression := subsplit[0]
|
||||
var err error
|
||||
parts[i*2-1], err = parseExpression(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts[i*2].raw = subsplit[1]
|
||||
}
|
||||
return &uriTemplate{
|
||||
raw: rawTemplate,
|
||||
parts: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type templatePart struct {
|
||||
raw string
|
||||
terms []templateTerm
|
||||
first string
|
||||
sep string
|
||||
named bool
|
||||
ifemp string
|
||||
allowReserved bool
|
||||
}
|
||||
|
||||
type templateTerm struct {
|
||||
name string
|
||||
explode bool
|
||||
truncate int
|
||||
}
|
||||
|
||||
func parseExpression(expression string) (result templatePart, err error) {
|
||||
switch expression[0] {
|
||||
case '+':
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
case '.':
|
||||
result.first = "."
|
||||
result.sep = "."
|
||||
expression = expression[1:]
|
||||
case '/':
|
||||
result.first = "/"
|
||||
result.sep = "/"
|
||||
expression = expression[1:]
|
||||
case ';':
|
||||
result.first = ";"
|
||||
result.sep = ";"
|
||||
result.named = true
|
||||
expression = expression[1:]
|
||||
case '?':
|
||||
result.first = "?"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '&':
|
||||
result.first = "&"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '#':
|
||||
result.first = "#"
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
default:
|
||||
result.sep = ","
|
||||
}
|
||||
rawterms := strings.Split(expression, ",")
|
||||
result.terms = make([]templateTerm, len(rawterms))
|
||||
for i, raw := range rawterms {
|
||||
result.terms[i], err = parseTerm(raw)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func parseTerm(term string) (result templateTerm, err error) {
|
||||
// TODO(djd): Remove "*" suffix parsing once we check that no APIs have
|
||||
// mistakenly used that attribute.
|
||||
if strings.HasSuffix(term, "*") {
|
||||
result.explode = true
|
||||
term = term[:len(term)-1]
|
||||
}
|
||||
split := strings.Split(term, ":")
|
||||
if len(split) == 1 {
|
||||
result.name = term
|
||||
} else if len(split) == 2 {
|
||||
result.name = split[0]
|
||||
var parsed int64
|
||||
parsed, err = strconv.ParseInt(split[1], 10, 0)
|
||||
result.truncate = int(parsed)
|
||||
} else {
|
||||
err = errors.New("multiple colons in same term")
|
||||
}
|
||||
if !validname.MatchString(result.name) {
|
||||
err = errors.New("not a valid name: " + result.name)
|
||||
}
|
||||
if result.explode && result.truncate > 0 {
|
||||
err = errors.New("both explode and prefix modifiers on same term")
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Expand expands a URI template with a set of values to produce the
|
||||
// resultant URI. Two forms of the result are returned: one with all the
|
||||
// elements escaped, and one with the elements unescaped.
|
||||
func (t *uriTemplate) Expand(values map[string]string) (escaped, unescaped string) {
|
||||
var w pairWriter
|
||||
for _, p := range t.parts {
|
||||
p.expand(&w, values)
|
||||
}
|
||||
return w.Escaped(), w.Unescaped()
|
||||
}
|
||||
|
||||
func (tp *templatePart) expand(w *pairWriter, values map[string]string) {
|
||||
if len(tp.raw) > 0 {
|
||||
w.Write(tp.raw)
|
||||
return
|
||||
}
|
||||
var first = true
|
||||
for _, term := range tp.terms {
|
||||
value, exists := values[term.name]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if first {
|
||||
w.Write(tp.first)
|
||||
first = false
|
||||
} else {
|
||||
w.Write(tp.sep)
|
||||
}
|
||||
tp.expandString(w, term, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *templatePart) expandName(w *pairWriter, name string, empty bool) {
|
||||
if tp.named {
|
||||
w.Write(name)
|
||||
if empty {
|
||||
w.Write(tp.ifemp)
|
||||
} else {
|
||||
w.Write("=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *templatePart) expandString(w *pairWriter, t templateTerm, s string) {
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
tp.expandName(w, t.name, len(s) == 0)
|
||||
w.Escape(s, tp.allowReserved)
|
||||
}
|
||||
17
vendor/google.golang.org/api/internal/third_party/uritemplates/utils.go
generated
vendored
Normal file
17
vendor/google.golang.org/api/internal/third_party/uritemplates/utils.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uritemplates
|
||||
|
||||
// Expand parses then expands a URI template with a set of values to produce
|
||||
// the resultant URI. Two forms of the result are returned: one with all the
|
||||
// elements escaped, and one with the elements unescaped.
|
||||
func Expand(path string, values map[string]string) (escaped, unescaped string, err error) {
|
||||
template, err := parse(path)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
escaped, unescaped = template.Expand(values)
|
||||
return escaped, unescaped, nil
|
||||
}
|
||||
8
vendor/google.golang.org/api/internal/version.go
generated
vendored
Normal file
8
vendor/google.golang.org/api/internal/version.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
// Version is the current tagged release of the library.
|
||||
const Version = "0.262.0"
|
||||
260
vendor/google.golang.org/api/oauth2/v1/oauth2-api.json
generated
vendored
Normal file
260
vendor/google.golang.org/api/oauth2/v1/oauth2-api.json
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/plus.me": {
|
||||
"description": "Associate you with your personal info on Google"
|
||||
},
|
||||
"https://www.googleapis.com/auth/userinfo.email": {
|
||||
"description": "View your email address"
|
||||
},
|
||||
"https://www.googleapis.com/auth/userinfo.profile": {
|
||||
"description": "See your personal info, including any personal info you've made publicly available"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"basePath": "/",
|
||||
"baseUrl": "https://www.googleapis.com/",
|
||||
"batchPath": "batch/oauth2/v1",
|
||||
"description": "Obtains end-user authorization grants for use with other Google APIs.",
|
||||
"discoveryVersion": "v1",
|
||||
"documentationLink": "https://developers.google.com/accounts/docs/OAuth2",
|
||||
"etag": "\"VPK3KBfpaEgZ16pozGOoMYfKc0U/Ydp-Ynsm-doIo9JKOgNSlCRTJVQ\"",
|
||||
"icons": {
|
||||
"x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png",
|
||||
"x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png"
|
||||
},
|
||||
"id": "oauth2:v1",
|
||||
"kind": "discovery#restDescription",
|
||||
"methods": {
|
||||
"tokeninfo": {
|
||||
"description": "Get token info",
|
||||
"httpMethod": "POST",
|
||||
"id": "oauth2.tokeninfo",
|
||||
"parameters": {
|
||||
"access_token": {
|
||||
"description": "The oauth2 access token",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"id_token": {
|
||||
"description": "The ID token",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "oauth2/v1/tokeninfo",
|
||||
"response": {
|
||||
"$ref": "Tokeninfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "oauth2",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Google",
|
||||
"parameters": {
|
||||
"alt": {
|
||||
"default": "json",
|
||||
"description": "Data format for the response.",
|
||||
"enum": [
|
||||
"json"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"Responses with Content-Type of application/json"
|
||||
],
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Selector specifying which fields to include in a partial response.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"oauth_token": {
|
||||
"description": "OAuth 2.0 token for the current user.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"prettyPrint": {
|
||||
"default": "true",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"location": "query",
|
||||
"type": "boolean"
|
||||
},
|
||||
"quotaUser": {
|
||||
"description": "An opaque string that represents a user for quota purposes. Must not exceed 40 characters.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"userIp": {
|
||||
"description": "Deprecated. Please use quotaUser instead.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"protocol": "rest",
|
||||
"resources": {
|
||||
"userinfo": {
|
||||
"methods": {
|
||||
"get": {
|
||||
"description": "Get user info",
|
||||
"httpMethod": "GET",
|
||||
"id": "oauth2.userinfo.get",
|
||||
"path": "oauth2/v1/userinfo",
|
||||
"response": {
|
||||
"$ref": "Userinfoplus"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/plus.me",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile"
|
||||
]
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"v2": {
|
||||
"resources": {
|
||||
"me": {
|
||||
"methods": {
|
||||
"get": {
|
||||
"description": "Get user info",
|
||||
"httpMethod": "GET",
|
||||
"id": "oauth2.userinfo.v2.me.get",
|
||||
"path": "userinfo/v2/me",
|
||||
"response": {
|
||||
"$ref": "Userinfoplus"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/plus.me",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"revision": "20190313",
|
||||
"rootUrl": "https://www.googleapis.com/",
|
||||
"schemas": {
|
||||
"Tokeninfo": {
|
||||
"id": "Tokeninfo",
|
||||
"properties": {
|
||||
"access_type": {
|
||||
"description": "The access type granted with this token. It can be offline or online.",
|
||||
"type": "string"
|
||||
},
|
||||
"audience": {
|
||||
"description": "Who is the intended audience for this token. In general the same as issued_to.",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "The email address of the user. Present only if the email scope is present in the request.",
|
||||
"type": "string"
|
||||
},
|
||||
"email_verified": {
|
||||
"description": "Boolean flag which is true if the email address is verified. Present only if the email scope is present in the request.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"expires_in": {
|
||||
"description": "The expiry time of the token, as number of seconds left until expiry.",
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"issued_at": {
|
||||
"description": "The issue time of the token, as number of seconds.",
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"issued_to": {
|
||||
"description": "To whom was the token issued to. In general the same as audience.",
|
||||
"type": "string"
|
||||
},
|
||||
"issuer": {
|
||||
"description": "Who issued the token.",
|
||||
"type": "string"
|
||||
},
|
||||
"nonce": {
|
||||
"description": "Nonce of the id token.",
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"description": "The space separated list of scopes granted to this token.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"description": "The obfuscated user id.",
|
||||
"type": "string"
|
||||
},
|
||||
"verified_email": {
|
||||
"description": "Boolean flag which is true if the email address is verified. Present only if the email scope is present in the request.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"Userinfoplus": {
|
||||
"id": "Userinfoplus",
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "The user's email address.",
|
||||
"type": "string"
|
||||
},
|
||||
"family_name": {
|
||||
"description": "The user's last name.",
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"description": "The user's gender.",
|
||||
"type": "string"
|
||||
},
|
||||
"given_name": {
|
||||
"description": "The user's first name.",
|
||||
"type": "string"
|
||||
},
|
||||
"hd": {
|
||||
"description": "The hosted domain e.g. example.com if the user is Google apps user.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "The obfuscated ID of the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"link": {
|
||||
"description": "URL of the profile page.",
|
||||
"type": "string"
|
||||
},
|
||||
"locale": {
|
||||
"description": "The user's preferred locale.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The user's full name.",
|
||||
"type": "string"
|
||||
},
|
||||
"picture": {
|
||||
"description": "URL of the user's picture image.",
|
||||
"type": "string"
|
||||
},
|
||||
"verified_email": {
|
||||
"default": "true",
|
||||
"description": "Boolean flag which is true if the email address is verified. Always verified because we only return the user's primary email address.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"servicePath": "",
|
||||
"title": "Google OAuth2 API",
|
||||
"version": "v1"
|
||||
}
|
||||
707
vendor/google.golang.org/api/oauth2/v1/oauth2-gen.go
generated
vendored
Normal file
707
vendor/google.golang.org/api/oauth2/v1/oauth2-gen.go
generated
vendored
Normal file
@@ -0,0 +1,707 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated file. DO NOT EDIT.
|
||||
|
||||
// Package oauth2 provides access to the Google OAuth2 API.
|
||||
//
|
||||
// For product documentation, see: https://developers.google.com/accounts/docs/OAuth2
|
||||
//
|
||||
// # Creating a client
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// import "google.golang.org/api/oauth2/v1"
|
||||
// ...
|
||||
// ctx := context.Background()
|
||||
// oauth2Service, err := oauth2.NewService(ctx)
|
||||
//
|
||||
// In this example, Google Application Default Credentials are used for authentication.
|
||||
//
|
||||
// For information on how to create and obtain Application Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials.
|
||||
//
|
||||
// # Other authentication options
|
||||
//
|
||||
// By default, all available scopes (see "Constants") are used to authenticate. To restrict scopes, use option.WithScopes:
|
||||
//
|
||||
// oauth2Service, err := oauth2.NewService(ctx, option.WithScopes(oauth2.UserinfoProfileScope))
|
||||
//
|
||||
// To use an API key for authentication (note: some APIs do not support API keys), use option.WithAPIKey:
|
||||
//
|
||||
// oauth2Service, err := oauth2.NewService(ctx, option.WithAPIKey("AIza..."))
|
||||
//
|
||||
// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth flow), use option.WithTokenSource:
|
||||
//
|
||||
// config := &oauth2.Config{...}
|
||||
// // ...
|
||||
// token, err := config.Exchange(ctx, ...)
|
||||
// oauth2Service, err := oauth2.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token)))
|
||||
//
|
||||
// See https://godoc.org/google.golang.org/api/option/ for details on options.
|
||||
package oauth2 // import "google.golang.org/api/oauth2/v1"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// Always reference these packages, just in case the auto-generated code
|
||||
// below doesn't.
|
||||
var _ = bytes.NewBuffer
|
||||
var _ = strconv.Itoa
|
||||
var _ = fmt.Sprintf
|
||||
var _ = json.NewDecoder
|
||||
var _ = io.Copy
|
||||
var _ = url.Parse
|
||||
var _ = gensupport.MarshalJSON
|
||||
var _ = googleapi.Version
|
||||
var _ = errors.New
|
||||
var _ = strings.Replace
|
||||
var _ = context.Canceled
|
||||
var _ = internaloption.WithDefaultEndpoint
|
||||
|
||||
const apiId = "oauth2:v1"
|
||||
const apiName = "oauth2"
|
||||
const apiVersion = "v1"
|
||||
const basePath = "https://www.googleapis.com/"
|
||||
|
||||
// OAuth2 scopes used by this API.
|
||||
const (
|
||||
// Associate you with your personal info on Google
|
||||
PlusMeScope = "https://www.googleapis.com/auth/plus.me"
|
||||
|
||||
// View your email address
|
||||
UserinfoEmailScope = "https://www.googleapis.com/auth/userinfo.email"
|
||||
|
||||
// See your personal info, including any personal info you've made
|
||||
// publicly available
|
||||
UserinfoProfileScope = "https://www.googleapis.com/auth/userinfo.profile"
|
||||
)
|
||||
|
||||
// NewService creates a new Service.
|
||||
func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
|
||||
scopesOption := option.WithScopes(
|
||||
"https://www.googleapis.com/auth/plus.me",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
)
|
||||
// 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
|
||||
}
|
||||
s, err := New(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if endpoint != "" {
|
||||
s.BasePath = endpoint
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// New creates a new Service. It uses the provided http.Client for requests.
|
||||
//
|
||||
// Deprecated: please use NewService instead.
|
||||
// To provide a custom HTTP client, use option.WithHTTPClient.
|
||||
// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead.
|
||||
func New(client *http.Client) (*Service, error) {
|
||||
if client == nil {
|
||||
return nil, errors.New("client is nil")
|
||||
}
|
||||
s := &Service{client: client, BasePath: basePath}
|
||||
s.Userinfo = NewUserinfoService(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
client *http.Client
|
||||
BasePath string // API endpoint base URL
|
||||
UserAgent string // optional additional User-Agent fragment
|
||||
|
||||
Userinfo *UserinfoService
|
||||
}
|
||||
|
||||
func (s *Service) userAgent() string {
|
||||
if s.UserAgent == "" {
|
||||
return googleapi.UserAgent
|
||||
}
|
||||
return googleapi.UserAgent + " " + s.UserAgent
|
||||
}
|
||||
|
||||
func NewUserinfoService(s *Service) *UserinfoService {
|
||||
rs := &UserinfoService{s: s}
|
||||
rs.V2 = NewUserinfoV2Service(s)
|
||||
return rs
|
||||
}
|
||||
|
||||
type UserinfoService struct {
|
||||
s *Service
|
||||
|
||||
V2 *UserinfoV2Service
|
||||
}
|
||||
|
||||
func NewUserinfoV2Service(s *Service) *UserinfoV2Service {
|
||||
rs := &UserinfoV2Service{s: s}
|
||||
rs.Me = NewUserinfoV2MeService(s)
|
||||
return rs
|
||||
}
|
||||
|
||||
type UserinfoV2Service struct {
|
||||
s *Service
|
||||
|
||||
Me *UserinfoV2MeService
|
||||
}
|
||||
|
||||
func NewUserinfoV2MeService(s *Service) *UserinfoV2MeService {
|
||||
rs := &UserinfoV2MeService{s: s}
|
||||
return rs
|
||||
}
|
||||
|
||||
type UserinfoV2MeService struct {
|
||||
s *Service
|
||||
}
|
||||
|
||||
type Tokeninfo struct {
|
||||
// AccessType: The access type granted with this token. It can be
|
||||
// offline or online.
|
||||
AccessType string `json:"access_type,omitempty"`
|
||||
|
||||
// Audience: Who is the intended audience for this token. In general the
|
||||
// same as issued_to.
|
||||
Audience string `json:"audience,omitempty"`
|
||||
|
||||
// Email: The email address of the user. Present only if the email scope
|
||||
// is present in the request.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
// EmailVerified: Boolean flag which is true if the email address is
|
||||
// verified. Present only if the email scope is present in the request.
|
||||
EmailVerified bool `json:"email_verified,omitempty"`
|
||||
|
||||
// ExpiresIn: The expiry time of the token, as number of seconds left
|
||||
// until expiry.
|
||||
ExpiresIn int64 `json:"expires_in,omitempty"`
|
||||
|
||||
// IssuedAt: The issue time of the token, as number of seconds.
|
||||
IssuedAt int64 `json:"issued_at,omitempty"`
|
||||
|
||||
// IssuedTo: To whom was the token issued to. In general the same as
|
||||
// audience.
|
||||
IssuedTo string `json:"issued_to,omitempty"`
|
||||
|
||||
// Issuer: Who issued the token.
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
|
||||
// Nonce: Nonce of the id token.
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
|
||||
// Scope: The space separated list of scopes granted to this token.
|
||||
Scope string `json:"scope,omitempty"`
|
||||
|
||||
// UserId: The obfuscated user id.
|
||||
UserId string `json:"user_id,omitempty"`
|
||||
|
||||
// VerifiedEmail: Boolean flag which is true if the email address is
|
||||
// verified. Present only if the email scope is present in the request.
|
||||
VerifiedEmail bool `json:"verified_email,omitempty"`
|
||||
|
||||
// ServerResponse contains the HTTP response code and headers from the
|
||||
// server.
|
||||
googleapi.ServerResponse `json:"-"`
|
||||
|
||||
// ForceSendFields is a list of field names (e.g. "AccessType") to
|
||||
// unconditionally include in API requests. By default, fields with
|
||||
// empty values are omitted from API requests. However, any non-pointer,
|
||||
// non-interface field appearing in ForceSendFields will be sent to the
|
||||
// server regardless of whether the field is empty or not. This may be
|
||||
// used to include empty fields in Patch requests.
|
||||
ForceSendFields []string `json:"-"`
|
||||
|
||||
// NullFields is a list of field names (e.g. "AccessType") to include in
|
||||
// API requests with the JSON null value. By default, fields with empty
|
||||
// values are omitted from API requests. However, any field with an
|
||||
// empty value appearing in NullFields will be sent to the server as
|
||||
// null. It is an error if a field in this list has a non-empty value.
|
||||
// This may be used to include null fields in Patch requests.
|
||||
NullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (s *Tokeninfo) MarshalJSON() ([]byte, error) {
|
||||
type NoMethod Tokeninfo
|
||||
raw := NoMethod(*s)
|
||||
return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
|
||||
}
|
||||
|
||||
type Userinfoplus struct {
|
||||
// Email: The user's email address.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
// FamilyName: The user's last name.
|
||||
FamilyName string `json:"family_name,omitempty"`
|
||||
|
||||
// Gender: The user's gender.
|
||||
Gender string `json:"gender,omitempty"`
|
||||
|
||||
// GivenName: The user's first name.
|
||||
GivenName string `json:"given_name,omitempty"`
|
||||
|
||||
// Hd: The hosted domain e.g. example.com if the user is Google apps
|
||||
// user.
|
||||
Hd string `json:"hd,omitempty"`
|
||||
|
||||
// Id: The obfuscated ID of the user.
|
||||
Id string `json:"id,omitempty"`
|
||||
|
||||
// Link: URL of the profile page.
|
||||
Link string `json:"link,omitempty"`
|
||||
|
||||
// Locale: The user's preferred locale.
|
||||
Locale string `json:"locale,omitempty"`
|
||||
|
||||
// Name: The user's full name.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Picture: URL of the user's picture image.
|
||||
Picture string `json:"picture,omitempty"`
|
||||
|
||||
// VerifiedEmail: Boolean flag which is true if the email address is
|
||||
// verified. Always verified because we only return the user's primary
|
||||
// email address.
|
||||
//
|
||||
// Default: true
|
||||
VerifiedEmail *bool `json:"verified_email,omitempty"`
|
||||
|
||||
// ServerResponse contains the HTTP response code and headers from the
|
||||
// server.
|
||||
googleapi.ServerResponse `json:"-"`
|
||||
|
||||
// ForceSendFields is a list of field names (e.g. "Email") to
|
||||
// unconditionally include in API requests. By default, fields with
|
||||
// empty values are omitted from API requests. However, any non-pointer,
|
||||
// non-interface field appearing in ForceSendFields will be sent to the
|
||||
// server regardless of whether the field is empty or not. This may be
|
||||
// used to include empty fields in Patch requests.
|
||||
ForceSendFields []string `json:"-"`
|
||||
|
||||
// NullFields is a list of field names (e.g. "Email") to include in API
|
||||
// requests with the JSON null value. By default, fields with empty
|
||||
// values are omitted from API requests. However, any field with an
|
||||
// empty value appearing in NullFields will be sent to the server as
|
||||
// null. It is an error if a field in this list has a non-empty value.
|
||||
// This may be used to include null fields in Patch requests.
|
||||
NullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (s *Userinfoplus) MarshalJSON() ([]byte, error) {
|
||||
type NoMethod Userinfoplus
|
||||
raw := NoMethod(*s)
|
||||
return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
|
||||
}
|
||||
|
||||
// method id "oauth2.tokeninfo":
|
||||
|
||||
type TokeninfoCall struct {
|
||||
s *Service
|
||||
urlParams_ gensupport.URLParams
|
||||
ctx_ context.Context
|
||||
header_ http.Header
|
||||
}
|
||||
|
||||
// Tokeninfo: Get token info
|
||||
func (s *Service) Tokeninfo() *TokeninfoCall {
|
||||
c := &TokeninfoCall{s: s, urlParams_: make(gensupport.URLParams)}
|
||||
return c
|
||||
}
|
||||
|
||||
// AccessToken sets the optional parameter "access_token": The oauth2
|
||||
// access token
|
||||
func (c *TokeninfoCall) AccessToken(accessToken string) *TokeninfoCall {
|
||||
c.urlParams_.Set("access_token", accessToken)
|
||||
return c
|
||||
}
|
||||
|
||||
// IdToken sets the optional parameter "id_token": The ID token
|
||||
func (c *TokeninfoCall) IdToken(idToken string) *TokeninfoCall {
|
||||
c.urlParams_.Set("id_token", idToken)
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved. See
|
||||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
|
||||
// for more information.
|
||||
func (c *TokeninfoCall) Fields(s ...googleapi.Field) *TokeninfoCall {
|
||||
c.urlParams_.Set("fields", googleapi.CombineFields(s))
|
||||
return c
|
||||
}
|
||||
|
||||
// Context sets the context to be used in this call's Do method. Any
|
||||
// pending HTTP request will be aborted if the provided context is
|
||||
// canceled.
|
||||
func (c *TokeninfoCall) Context(ctx context.Context) *TokeninfoCall {
|
||||
c.ctx_ = ctx
|
||||
return c
|
||||
}
|
||||
|
||||
// Header returns an http.Header that can be modified by the caller to
|
||||
// add HTTP headers to the request.
|
||||
func (c *TokeninfoCall) Header() http.Header {
|
||||
if c.header_ == nil {
|
||||
c.header_ = make(http.Header)
|
||||
}
|
||||
return c.header_
|
||||
}
|
||||
|
||||
func (c *TokeninfoCall) doRequest(alt string) (*http.Response, error) {
|
||||
reqHeaders := make(http.Header)
|
||||
reqHeaders.Set("x-goog-api-client", "gl-go/"+gensupport.GoVersion()+" gdcl/20200317")
|
||||
for k, v := range c.header_ {
|
||||
reqHeaders[k] = v
|
||||
}
|
||||
reqHeaders.Set("User-Agent", c.s.userAgent())
|
||||
var body io.Reader = nil
|
||||
c.urlParams_.Set("alt", alt)
|
||||
c.urlParams_.Set("prettyPrint", "false")
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "oauth2/v1/tokeninfo")
|
||||
urls += "?" + c.urlParams_.Encode()
|
||||
req, err := http.NewRequest("POST", urls, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
return gensupport.SendRequest(c.ctx_, c.s.client, req)
|
||||
}
|
||||
|
||||
// Do executes the "oauth2.tokeninfo" call.
|
||||
// Exactly one of *Tokeninfo or error will be non-nil. Any non-2xx
|
||||
// status code is an error. Response headers are in either
|
||||
// *Tokeninfo.ServerResponse.Header or (if a response was returned at
|
||||
// all) in error.(*googleapi.Error).Header. Use googleapi.IsNotModified
|
||||
// to check whether the returned error was because
|
||||
// http.StatusNotModified was returned.
|
||||
func (c *TokeninfoCall) Do(opts ...googleapi.CallOption) (*Tokeninfo, error) {
|
||||
gensupport.SetOptions(c.urlParams_, opts...)
|
||||
res, err := c.doRequest("json")
|
||||
if res != nil && res.StatusCode == http.StatusNotModified {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
return nil, &googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &Tokeninfo{
|
||||
ServerResponse: googleapi.ServerResponse{
|
||||
Header: res.Header,
|
||||
HTTPStatusCode: res.StatusCode,
|
||||
},
|
||||
}
|
||||
target := &ret
|
||||
if err := gensupport.DecodeResponse(target, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
// {
|
||||
// "description": "Get token info",
|
||||
// "httpMethod": "POST",
|
||||
// "id": "oauth2.tokeninfo",
|
||||
// "parameters": {
|
||||
// "access_token": {
|
||||
// "description": "The oauth2 access token",
|
||||
// "location": "query",
|
||||
// "type": "string"
|
||||
// },
|
||||
// "id_token": {
|
||||
// "description": "The ID token",
|
||||
// "location": "query",
|
||||
// "type": "string"
|
||||
// }
|
||||
// },
|
||||
// "path": "oauth2/v1/tokeninfo",
|
||||
// "response": {
|
||||
// "$ref": "Tokeninfo"
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// method id "oauth2.userinfo.get":
|
||||
|
||||
type UserinfoGetCall struct {
|
||||
s *Service
|
||||
urlParams_ gensupport.URLParams
|
||||
ifNoneMatch_ string
|
||||
ctx_ context.Context
|
||||
header_ http.Header
|
||||
}
|
||||
|
||||
// Get: Get user info
|
||||
func (r *UserinfoService) Get() *UserinfoGetCall {
|
||||
c := &UserinfoGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved. See
|
||||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
|
||||
// for more information.
|
||||
func (c *UserinfoGetCall) Fields(s ...googleapi.Field) *UserinfoGetCall {
|
||||
c.urlParams_.Set("fields", googleapi.CombineFields(s))
|
||||
return c
|
||||
}
|
||||
|
||||
// IfNoneMatch sets the optional parameter which makes the operation
|
||||
// fail if the object's ETag matches the given value. This is useful for
|
||||
// getting updates only after the object has changed since the last
|
||||
// request. Use googleapi.IsNotModified to check whether the response
|
||||
// error from Do is the result of In-None-Match.
|
||||
func (c *UserinfoGetCall) IfNoneMatch(entityTag string) *UserinfoGetCall {
|
||||
c.ifNoneMatch_ = entityTag
|
||||
return c
|
||||
}
|
||||
|
||||
// Context sets the context to be used in this call's Do method. Any
|
||||
// pending HTTP request will be aborted if the provided context is
|
||||
// canceled.
|
||||
func (c *UserinfoGetCall) Context(ctx context.Context) *UserinfoGetCall {
|
||||
c.ctx_ = ctx
|
||||
return c
|
||||
}
|
||||
|
||||
// Header returns an http.Header that can be modified by the caller to
|
||||
// add HTTP headers to the request.
|
||||
func (c *UserinfoGetCall) Header() http.Header {
|
||||
if c.header_ == nil {
|
||||
c.header_ = make(http.Header)
|
||||
}
|
||||
return c.header_
|
||||
}
|
||||
|
||||
func (c *UserinfoGetCall) doRequest(alt string) (*http.Response, error) {
|
||||
reqHeaders := make(http.Header)
|
||||
reqHeaders.Set("x-goog-api-client", "gl-go/"+gensupport.GoVersion()+" gdcl/20200317")
|
||||
for k, v := range c.header_ {
|
||||
reqHeaders[k] = v
|
||||
}
|
||||
reqHeaders.Set("User-Agent", c.s.userAgent())
|
||||
if c.ifNoneMatch_ != "" {
|
||||
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
|
||||
}
|
||||
var body io.Reader = nil
|
||||
c.urlParams_.Set("alt", alt)
|
||||
c.urlParams_.Set("prettyPrint", "false")
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "oauth2/v1/userinfo")
|
||||
urls += "?" + c.urlParams_.Encode()
|
||||
req, err := http.NewRequest("GET", urls, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
return gensupport.SendRequest(c.ctx_, c.s.client, req)
|
||||
}
|
||||
|
||||
// Do executes the "oauth2.userinfo.get" call.
|
||||
// Exactly one of *Userinfoplus or error will be non-nil. Any non-2xx
|
||||
// status code is an error. Response headers are in either
|
||||
// *Userinfoplus.ServerResponse.Header or (if a response was returned at
|
||||
// all) in error.(*googleapi.Error).Header. Use googleapi.IsNotModified
|
||||
// to check whether the returned error was because
|
||||
// http.StatusNotModified was returned.
|
||||
func (c *UserinfoGetCall) Do(opts ...googleapi.CallOption) (*Userinfoplus, error) {
|
||||
gensupport.SetOptions(c.urlParams_, opts...)
|
||||
res, err := c.doRequest("json")
|
||||
if res != nil && res.StatusCode == http.StatusNotModified {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
return nil, &googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &Userinfoplus{
|
||||
ServerResponse: googleapi.ServerResponse{
|
||||
Header: res.Header,
|
||||
HTTPStatusCode: res.StatusCode,
|
||||
},
|
||||
}
|
||||
target := &ret
|
||||
if err := gensupport.DecodeResponse(target, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
// {
|
||||
// "description": "Get user info",
|
||||
// "httpMethod": "GET",
|
||||
// "id": "oauth2.userinfo.get",
|
||||
// "path": "oauth2/v1/userinfo",
|
||||
// "response": {
|
||||
// "$ref": "Userinfoplus"
|
||||
// },
|
||||
// "scopes": [
|
||||
// "https://www.googleapis.com/auth/plus.me",
|
||||
// "https://www.googleapis.com/auth/userinfo.email",
|
||||
// "https://www.googleapis.com/auth/userinfo.profile"
|
||||
// ]
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// method id "oauth2.userinfo.v2.me.get":
|
||||
|
||||
type UserinfoV2MeGetCall struct {
|
||||
s *Service
|
||||
urlParams_ gensupport.URLParams
|
||||
ifNoneMatch_ string
|
||||
ctx_ context.Context
|
||||
header_ http.Header
|
||||
}
|
||||
|
||||
// Get: Get user info
|
||||
func (r *UserinfoV2MeService) Get() *UserinfoV2MeGetCall {
|
||||
c := &UserinfoV2MeGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved. See
|
||||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
|
||||
// for more information.
|
||||
func (c *UserinfoV2MeGetCall) Fields(s ...googleapi.Field) *UserinfoV2MeGetCall {
|
||||
c.urlParams_.Set("fields", googleapi.CombineFields(s))
|
||||
return c
|
||||
}
|
||||
|
||||
// IfNoneMatch sets the optional parameter which makes the operation
|
||||
// fail if the object's ETag matches the given value. This is useful for
|
||||
// getting updates only after the object has changed since the last
|
||||
// request. Use googleapi.IsNotModified to check whether the response
|
||||
// error from Do is the result of In-None-Match.
|
||||
func (c *UserinfoV2MeGetCall) IfNoneMatch(entityTag string) *UserinfoV2MeGetCall {
|
||||
c.ifNoneMatch_ = entityTag
|
||||
return c
|
||||
}
|
||||
|
||||
// Context sets the context to be used in this call's Do method. Any
|
||||
// pending HTTP request will be aborted if the provided context is
|
||||
// canceled.
|
||||
func (c *UserinfoV2MeGetCall) Context(ctx context.Context) *UserinfoV2MeGetCall {
|
||||
c.ctx_ = ctx
|
||||
return c
|
||||
}
|
||||
|
||||
// Header returns an http.Header that can be modified by the caller to
|
||||
// add HTTP headers to the request.
|
||||
func (c *UserinfoV2MeGetCall) Header() http.Header {
|
||||
if c.header_ == nil {
|
||||
c.header_ = make(http.Header)
|
||||
}
|
||||
return c.header_
|
||||
}
|
||||
|
||||
func (c *UserinfoV2MeGetCall) doRequest(alt string) (*http.Response, error) {
|
||||
reqHeaders := make(http.Header)
|
||||
reqHeaders.Set("x-goog-api-client", "gl-go/"+gensupport.GoVersion()+" gdcl/20200317")
|
||||
for k, v := range c.header_ {
|
||||
reqHeaders[k] = v
|
||||
}
|
||||
reqHeaders.Set("User-Agent", c.s.userAgent())
|
||||
if c.ifNoneMatch_ != "" {
|
||||
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
|
||||
}
|
||||
var body io.Reader = nil
|
||||
c.urlParams_.Set("alt", alt)
|
||||
c.urlParams_.Set("prettyPrint", "false")
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "userinfo/v2/me")
|
||||
urls += "?" + c.urlParams_.Encode()
|
||||
req, err := http.NewRequest("GET", urls, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
return gensupport.SendRequest(c.ctx_, c.s.client, req)
|
||||
}
|
||||
|
||||
// Do executes the "oauth2.userinfo.v2.me.get" call.
|
||||
// Exactly one of *Userinfoplus or error will be non-nil. Any non-2xx
|
||||
// status code is an error. Response headers are in either
|
||||
// *Userinfoplus.ServerResponse.Header or (if a response was returned at
|
||||
// all) in error.(*googleapi.Error).Header. Use googleapi.IsNotModified
|
||||
// to check whether the returned error was because
|
||||
// http.StatusNotModified was returned.
|
||||
func (c *UserinfoV2MeGetCall) Do(opts ...googleapi.CallOption) (*Userinfoplus, error) {
|
||||
gensupport.SetOptions(c.urlParams_, opts...)
|
||||
res, err := c.doRequest("json")
|
||||
if res != nil && res.StatusCode == http.StatusNotModified {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
return nil, &googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &Userinfoplus{
|
||||
ServerResponse: googleapi.ServerResponse{
|
||||
Header: res.Header,
|
||||
HTTPStatusCode: res.StatusCode,
|
||||
},
|
||||
}
|
||||
target := &ret
|
||||
if err := gensupport.DecodeResponse(target, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
// {
|
||||
// "description": "Get user info",
|
||||
// "httpMethod": "GET",
|
||||
// "id": "oauth2.userinfo.v2.me.get",
|
||||
// "path": "userinfo/v2/me",
|
||||
// "response": {
|
||||
// "$ref": "Userinfoplus"
|
||||
// },
|
||||
// "scopes": [
|
||||
// "https://www.googleapis.com/auth/plus.me",
|
||||
// "https://www.googleapis.com/auth/userinfo.email",
|
||||
// "https://www.googleapis.com/auth/userinfo.profile"
|
||||
// ]
|
||||
// }
|
||||
|
||||
}
|
||||
234
vendor/google.golang.org/api/oauth2/v2/oauth2-api.json
generated
vendored
Normal file
234
vendor/google.golang.org/api/oauth2/v2/oauth2-api.json
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/userinfo.email": {
|
||||
"description": "See your primary Google Account email address"
|
||||
},
|
||||
"https://www.googleapis.com/auth/userinfo.profile": {
|
||||
"description": "See your personal info, including any personal info you've made publicly available"
|
||||
},
|
||||
"openid": {
|
||||
"description": "Associate you with your personal info on Google"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"basePath": "/",
|
||||
"baseUrl": "https://www.googleapis.com/",
|
||||
"batchPath": "batch/oauth2/v2",
|
||||
"description": "Obtains end-user authorization grants for use with other Google APIs.",
|
||||
"discoveryVersion": "v1",
|
||||
"documentationLink": "https://developers.google.com/identity/protocols/oauth2/",
|
||||
"etag": "\"u9GIe6H63LSGq-9_t39K2Zx_EAc/VCyF6WfWVwIuhIs_gw3LA4B3w1E\"",
|
||||
"icons": {
|
||||
"x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png",
|
||||
"x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png"
|
||||
},
|
||||
"id": "oauth2:v2",
|
||||
"kind": "discovery#restDescription",
|
||||
"methods": {
|
||||
"tokeninfo": {
|
||||
"httpMethod": "POST",
|
||||
"id": "oauth2.tokeninfo",
|
||||
"parameters": {
|
||||
"access_token": {
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"id_token": {
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "oauth2/v2/tokeninfo",
|
||||
"response": {
|
||||
"$ref": "Tokeninfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "oauth2",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Google",
|
||||
"parameters": {
|
||||
"alt": {
|
||||
"default": "json",
|
||||
"description": "Data format for the response.",
|
||||
"enum": [
|
||||
"json"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"Responses with Content-Type of application/json"
|
||||
],
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Selector specifying which fields to include in a partial response.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"oauth_token": {
|
||||
"description": "OAuth 2.0 token for the current user.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"prettyPrint": {
|
||||
"default": "true",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"location": "query",
|
||||
"type": "boolean"
|
||||
},
|
||||
"quotaUser": {
|
||||
"description": "An opaque string that represents a user for quota purposes. Must not exceed 40 characters.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"userIp": {
|
||||
"description": "Deprecated. Please use quotaUser instead.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"protocol": "rest",
|
||||
"resources": {
|
||||
"userinfo": {
|
||||
"methods": {
|
||||
"get": {
|
||||
"httpMethod": "GET",
|
||||
"id": "oauth2.userinfo.get",
|
||||
"path": "oauth2/v2/userinfo",
|
||||
"response": {
|
||||
"$ref": "Userinfo"
|
||||
},
|
||||
"scopes": [
|
||||
"openid",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile"
|
||||
]
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"v2": {
|
||||
"resources": {
|
||||
"me": {
|
||||
"methods": {
|
||||
"get": {
|
||||
"httpMethod": "GET",
|
||||
"id": "oauth2.userinfo.v2.me.get",
|
||||
"path": "userinfo/v2/me",
|
||||
"response": {
|
||||
"$ref": "Userinfo"
|
||||
},
|
||||
"scopes": [
|
||||
"openid",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"revision": "20200213",
|
||||
"rootUrl": "https://www.googleapis.com/",
|
||||
"schemas": {
|
||||
"Tokeninfo": {
|
||||
"id": "Tokeninfo",
|
||||
"properties": {
|
||||
"audience": {
|
||||
"description": "Who is the intended audience for this token. In general the same as issued_to.",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "The email address of the user. Present only if the email scope is present in the request.",
|
||||
"type": "string"
|
||||
},
|
||||
"expires_in": {
|
||||
"description": "The expiry time of the token, as number of seconds left until expiry.",
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"issued_to": {
|
||||
"description": "To whom was the token issued to. In general the same as audience.",
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"description": "The space separated list of scopes granted to this token.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"description": "The obfuscated user id.",
|
||||
"type": "string"
|
||||
},
|
||||
"verified_email": {
|
||||
"description": "Boolean flag which is true if the email address is verified. Present only if the email scope is present in the request.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"Userinfo": {
|
||||
"id": "Userinfo",
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "The user's email address.",
|
||||
"type": "string"
|
||||
},
|
||||
"family_name": {
|
||||
"description": "The user's last name.",
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"description": "The user's gender.",
|
||||
"type": "string"
|
||||
},
|
||||
"given_name": {
|
||||
"description": "The user's first name.",
|
||||
"type": "string"
|
||||
},
|
||||
"hd": {
|
||||
"description": "The hosted domain e.g. example.com if the user is Google apps user.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "The obfuscated ID of the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"link": {
|
||||
"description": "URL of the profile page.",
|
||||
"type": "string"
|
||||
},
|
||||
"locale": {
|
||||
"description": "The user's preferred locale.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The user's full name.",
|
||||
"type": "string"
|
||||
},
|
||||
"picture": {
|
||||
"description": "URL of the user's picture image.",
|
||||
"type": "string"
|
||||
},
|
||||
"verified_email": {
|
||||
"default": "true",
|
||||
"description": "Boolean flag which is true if the email address is verified. Always verified because we only return the user's primary email address.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"servicePath": "",
|
||||
"title": "Google OAuth2 API",
|
||||
"version": "v2"
|
||||
}
|
||||
592
vendor/google.golang.org/api/oauth2/v2/oauth2-gen.go
generated
vendored
Normal file
592
vendor/google.golang.org/api/oauth2/v2/oauth2-gen.go
generated
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
// Copyright 2026 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated file. DO NOT EDIT.
|
||||
|
||||
// Package oauth2 provides access to the Google OAuth2 API.
|
||||
//
|
||||
// For product documentation, see: https://developers.google.com/identity/protocols/oauth2/
|
||||
//
|
||||
// # Library status
|
||||
//
|
||||
// These client libraries are officially supported by Google. However, this
|
||||
// library is considered complete and is in maintenance mode. This means
|
||||
// that we will address critical bugs and security issues but will not add
|
||||
// any new features.
|
||||
//
|
||||
// When possible, we recommend using our newer
|
||||
// [Cloud Client Libraries for Go](https://pkg.go.dev/cloud.google.com/go)
|
||||
// that are still actively being worked and iterated on.
|
||||
//
|
||||
// # Creating a client
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// import "google.golang.org/api/oauth2/v2"
|
||||
// ...
|
||||
// ctx := context.Background()
|
||||
// oauth2Service, err := oauth2.NewService(ctx)
|
||||
//
|
||||
// In this example, Google Application Default Credentials are used for
|
||||
// authentication. For information on how to create and obtain Application
|
||||
// Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials.
|
||||
//
|
||||
// # Other authentication options
|
||||
//
|
||||
// By default, all available scopes (see "Constants") are used to authenticate.
|
||||
// To restrict scopes, use [google.golang.org/api/option.WithScopes]:
|
||||
//
|
||||
// oauth2Service, err := oauth2.NewService(ctx, option.WithScopes(oauth2.OpenIDScope))
|
||||
//
|
||||
// To use an API key for authentication (note: some APIs do not support API
|
||||
// keys), use [google.golang.org/api/option.WithAPIKey]:
|
||||
//
|
||||
// oauth2Service, err := oauth2.NewService(ctx, option.WithAPIKey("AIza..."))
|
||||
//
|
||||
// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth
|
||||
// flow, use [google.golang.org/api/option.WithTokenSource]:
|
||||
//
|
||||
// config := &oauth2.Config{...}
|
||||
// // ...
|
||||
// token, err := config.Exchange(ctx, ...)
|
||||
// oauth2Service, err := oauth2.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token)))
|
||||
//
|
||||
// See [google.golang.org/api/option.ClientOption] for details on options.
|
||||
package oauth2 // import "google.golang.org/api/oauth2/v2"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/googleapis/gax-go/v2/internallog"
|
||||
googleapi "google.golang.org/api/googleapi"
|
||||
internal "google.golang.org/api/internal"
|
||||
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"
|
||||
)
|
||||
|
||||
// Always reference these packages, just in case the auto-generated code
|
||||
// below doesn't.
|
||||
var _ = bytes.NewBuffer
|
||||
var _ = strconv.Itoa
|
||||
var _ = fmt.Sprintf
|
||||
var _ = json.NewDecoder
|
||||
var _ = io.Copy
|
||||
var _ = url.Parse
|
||||
var _ = gensupport.MarshalJSON
|
||||
var _ = googleapi.Version
|
||||
var _ = errors.New
|
||||
var _ = strings.Replace
|
||||
var _ = context.Canceled
|
||||
var _ = internaloption.WithDefaultEndpoint
|
||||
var _ = internal.Version
|
||||
var _ = internallog.New
|
||||
|
||||
const apiId = "oauth2:v2"
|
||||
const apiName = "oauth2"
|
||||
const apiVersion = "v2"
|
||||
const basePath = "https://www.googleapis.com/"
|
||||
const basePathTemplate = "https://www.UNIVERSE_DOMAIN/"
|
||||
|
||||
// OAuth2 scopes used by this API.
|
||||
const (
|
||||
// See your primary Google Account email address
|
||||
UserinfoEmailScope = "https://www.googleapis.com/auth/userinfo.email"
|
||||
|
||||
// See your personal info, including any personal info you've made publicly
|
||||
// available
|
||||
UserinfoProfileScope = "https://www.googleapis.com/auth/userinfo.profile"
|
||||
|
||||
// Associate you with your personal info on Google
|
||||
OpenIDScope = "openid"
|
||||
)
|
||||
|
||||
// NewService creates a new Service.
|
||||
func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
|
||||
scopesOption := internaloption.WithDefaultScopes(
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"openid",
|
||||
)
|
||||
// NOTE: prepend, so we don't override user-specified scopes.
|
||||
opts = append([]option.ClientOption{scopesOption}, opts...)
|
||||
opts = append(opts, internaloption.WithDefaultEndpoint(basePath))
|
||||
opts = append(opts, internaloption.WithDefaultEndpointTemplate(basePathTemplate))
|
||||
opts = append(opts, internaloption.EnableNewAuthLibrary())
|
||||
client, endpoint, err := htransport.NewClient(ctx, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Service{client: client, BasePath: basePath, logger: internaloption.GetLogger(opts)}
|
||||
s.Userinfo = NewUserinfoService(s)
|
||||
if endpoint != "" {
|
||||
s.BasePath = endpoint
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// New creates a new Service. It uses the provided http.Client for requests.
|
||||
//
|
||||
// Deprecated: please use NewService instead.
|
||||
// To provide a custom HTTP client, use option.WithHTTPClient.
|
||||
// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead.
|
||||
func New(client *http.Client) (*Service, error) {
|
||||
if client == nil {
|
||||
return nil, errors.New("client is nil")
|
||||
}
|
||||
return NewService(context.TODO(), option.WithHTTPClient(client))
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
client *http.Client
|
||||
logger *slog.Logger
|
||||
BasePath string // API endpoint base URL
|
||||
UserAgent string // optional additional User-Agent fragment
|
||||
|
||||
Userinfo *UserinfoService
|
||||
}
|
||||
|
||||
func (s *Service) userAgent() string {
|
||||
if s.UserAgent == "" {
|
||||
return googleapi.UserAgent
|
||||
}
|
||||
return googleapi.UserAgent + " " + s.UserAgent
|
||||
}
|
||||
|
||||
func NewUserinfoService(s *Service) *UserinfoService {
|
||||
rs := &UserinfoService{s: s}
|
||||
rs.V2 = NewUserinfoV2Service(s)
|
||||
return rs
|
||||
}
|
||||
|
||||
type UserinfoService struct {
|
||||
s *Service
|
||||
|
||||
V2 *UserinfoV2Service
|
||||
}
|
||||
|
||||
func NewUserinfoV2Service(s *Service) *UserinfoV2Service {
|
||||
rs := &UserinfoV2Service{s: s}
|
||||
rs.Me = NewUserinfoV2MeService(s)
|
||||
return rs
|
||||
}
|
||||
|
||||
type UserinfoV2Service struct {
|
||||
s *Service
|
||||
|
||||
Me *UserinfoV2MeService
|
||||
}
|
||||
|
||||
func NewUserinfoV2MeService(s *Service) *UserinfoV2MeService {
|
||||
rs := &UserinfoV2MeService{s: s}
|
||||
return rs
|
||||
}
|
||||
|
||||
type UserinfoV2MeService struct {
|
||||
s *Service
|
||||
}
|
||||
|
||||
type Tokeninfo struct {
|
||||
// Audience: Who is the intended audience for this token. In general the same
|
||||
// as issued_to.
|
||||
Audience string `json:"audience,omitempty"`
|
||||
// Email: The email address of the user. Present only if the email scope is
|
||||
// present in the request.
|
||||
Email string `json:"email,omitempty"`
|
||||
// ExpiresIn: The expiry time of the token, as number of seconds left until
|
||||
// expiry.
|
||||
ExpiresIn int64 `json:"expires_in,omitempty"`
|
||||
// IssuedTo: To whom was the token issued to. In general the same as audience.
|
||||
IssuedTo string `json:"issued_to,omitempty"`
|
||||
// Scope: The space separated list of scopes granted to this token.
|
||||
Scope string `json:"scope,omitempty"`
|
||||
// UserId: The obfuscated user id.
|
||||
UserId string `json:"user_id,omitempty"`
|
||||
// VerifiedEmail: Boolean flag which is true if the email address is verified.
|
||||
// Present only if the email scope is present in the request.
|
||||
VerifiedEmail bool `json:"verified_email,omitempty"`
|
||||
|
||||
// ServerResponse contains the HTTP response code and headers from the server.
|
||||
googleapi.ServerResponse `json:"-"`
|
||||
// ForceSendFields is a list of field names (e.g. "Audience") to
|
||||
// unconditionally include in API requests. By default, fields with empty or
|
||||
// default values are omitted from API requests. See
|
||||
// https://pkg.go.dev/google.golang.org/api#hdr-ForceSendFields for more
|
||||
// details.
|
||||
ForceSendFields []string `json:"-"`
|
||||
// NullFields is a list of field names (e.g. "Audience") to include in API
|
||||
// requests with the JSON null value. By default, fields with empty values are
|
||||
// omitted from API requests. See
|
||||
// https://pkg.go.dev/google.golang.org/api#hdr-NullFields for more details.
|
||||
NullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (s Tokeninfo) MarshalJSON() ([]byte, error) {
|
||||
type NoMethod Tokeninfo
|
||||
return gensupport.MarshalJSON(NoMethod(s), s.ForceSendFields, s.NullFields)
|
||||
}
|
||||
|
||||
type Userinfo struct {
|
||||
// Email: The user's email address.
|
||||
Email string `json:"email,omitempty"`
|
||||
// FamilyName: The user's last name.
|
||||
FamilyName string `json:"family_name,omitempty"`
|
||||
// Gender: The user's gender.
|
||||
Gender string `json:"gender,omitempty"`
|
||||
// GivenName: The user's first name.
|
||||
GivenName string `json:"given_name,omitempty"`
|
||||
// Hd: The hosted domain e.g. example.com if the user is Google apps user.
|
||||
Hd string `json:"hd,omitempty"`
|
||||
// Id: The obfuscated ID of the user.
|
||||
Id string `json:"id,omitempty"`
|
||||
// Link: URL of the profile page.
|
||||
Link string `json:"link,omitempty"`
|
||||
// Locale: The user's preferred locale.
|
||||
Locale string `json:"locale,omitempty"`
|
||||
// Name: The user's full name.
|
||||
Name string `json:"name,omitempty"`
|
||||
// Picture: URL of the user's picture image.
|
||||
Picture string `json:"picture,omitempty"`
|
||||
// VerifiedEmail: Boolean flag which is true if the email address is verified.
|
||||
// Always verified because we only return the user's primary email address.
|
||||
//
|
||||
// Default: true
|
||||
VerifiedEmail *bool `json:"verified_email,omitempty"`
|
||||
|
||||
// ServerResponse contains the HTTP response code and headers from the server.
|
||||
googleapi.ServerResponse `json:"-"`
|
||||
// ForceSendFields is a list of field names (e.g. "Email") to unconditionally
|
||||
// include in API requests. By default, fields with empty or default values are
|
||||
// omitted from API requests. See
|
||||
// https://pkg.go.dev/google.golang.org/api#hdr-ForceSendFields for more
|
||||
// details.
|
||||
ForceSendFields []string `json:"-"`
|
||||
// NullFields is a list of field names (e.g. "Email") to include in API
|
||||
// requests with the JSON null value. By default, fields with empty values are
|
||||
// omitted from API requests. See
|
||||
// https://pkg.go.dev/google.golang.org/api#hdr-NullFields for more details.
|
||||
NullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (s Userinfo) MarshalJSON() ([]byte, error) {
|
||||
type NoMethod Userinfo
|
||||
return gensupport.MarshalJSON(NoMethod(s), s.ForceSendFields, s.NullFields)
|
||||
}
|
||||
|
||||
type TokeninfoCall struct {
|
||||
s *Service
|
||||
urlParams_ gensupport.URLParams
|
||||
ctx_ context.Context
|
||||
header_ http.Header
|
||||
}
|
||||
|
||||
// Tokeninfo:
|
||||
func (s *Service) Tokeninfo() *TokeninfoCall {
|
||||
c := &TokeninfoCall{s: s, urlParams_: make(gensupport.URLParams)}
|
||||
return c
|
||||
}
|
||||
|
||||
// AccessToken sets the optional parameter "access_token":
|
||||
func (c *TokeninfoCall) AccessToken(accessToken string) *TokeninfoCall {
|
||||
c.urlParams_.Set("access_token", accessToken)
|
||||
return c
|
||||
}
|
||||
|
||||
// IdToken sets the optional parameter "id_token":
|
||||
func (c *TokeninfoCall) IdToken(idToken string) *TokeninfoCall {
|
||||
c.urlParams_.Set("id_token", idToken)
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved. See
|
||||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse for more
|
||||
// details.
|
||||
func (c *TokeninfoCall) Fields(s ...googleapi.Field) *TokeninfoCall {
|
||||
c.urlParams_.Set("fields", googleapi.CombineFields(s))
|
||||
return c
|
||||
}
|
||||
|
||||
// Context sets the context to be used in this call's Do method.
|
||||
func (c *TokeninfoCall) Context(ctx context.Context) *TokeninfoCall {
|
||||
c.ctx_ = ctx
|
||||
return c
|
||||
}
|
||||
|
||||
// Header returns a http.Header that can be modified by the caller to add
|
||||
// headers to the request.
|
||||
func (c *TokeninfoCall) Header() http.Header {
|
||||
if c.header_ == nil {
|
||||
c.header_ = make(http.Header)
|
||||
}
|
||||
return c.header_
|
||||
}
|
||||
|
||||
func (c *TokeninfoCall) doRequest(alt string) (*http.Response, error) {
|
||||
reqHeaders := gensupport.SetHeaders(c.s.userAgent(), "", c.header_)
|
||||
c.urlParams_.Set("alt", alt)
|
||||
c.urlParams_.Set("prettyPrint", "false")
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "oauth2/v2/tokeninfo")
|
||||
urls += "?" + c.urlParams_.Encode()
|
||||
req, err := http.NewRequest("POST", urls, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
c.s.logger.DebugContext(c.ctx_, "api request", "serviceName", apiName, "rpcName", "oauth2.tokeninfo", "request", internallog.HTTPRequest(req, nil))
|
||||
return gensupport.SendRequest(c.ctx_, c.s.client, req)
|
||||
}
|
||||
|
||||
// Do executes the "oauth2.tokeninfo" call.
|
||||
// Any non-2xx status code is an error. Response headers are in either
|
||||
// *Tokeninfo.ServerResponse.Header or (if a response was returned at all) in
|
||||
// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
|
||||
// whether the returned error was because http.StatusNotModified was returned.
|
||||
func (c *TokeninfoCall) Do(opts ...googleapi.CallOption) (*Tokeninfo, error) {
|
||||
gensupport.SetOptions(c.urlParams_, opts...)
|
||||
res, err := c.doRequest("json")
|
||||
if res != nil && res.StatusCode == http.StatusNotModified {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
return nil, gensupport.WrapError(&googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, gensupport.WrapError(err)
|
||||
}
|
||||
ret := &Tokeninfo{
|
||||
ServerResponse: googleapi.ServerResponse{
|
||||
Header: res.Header,
|
||||
HTTPStatusCode: res.StatusCode,
|
||||
},
|
||||
}
|
||||
target := &ret
|
||||
b, err := gensupport.DecodeResponseBytes(target, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.s.logger.DebugContext(c.ctx_, "api response", "serviceName", apiName, "rpcName", "oauth2.tokeninfo", "response", internallog.HTTPResponse(res, b))
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type UserinfoGetCall struct {
|
||||
s *Service
|
||||
urlParams_ gensupport.URLParams
|
||||
ifNoneMatch_ string
|
||||
ctx_ context.Context
|
||||
header_ http.Header
|
||||
}
|
||||
|
||||
// Get:
|
||||
func (r *UserinfoService) Get() *UserinfoGetCall {
|
||||
c := &UserinfoGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved. See
|
||||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse for more
|
||||
// details.
|
||||
func (c *UserinfoGetCall) Fields(s ...googleapi.Field) *UserinfoGetCall {
|
||||
c.urlParams_.Set("fields", googleapi.CombineFields(s))
|
||||
return c
|
||||
}
|
||||
|
||||
// IfNoneMatch sets an optional parameter which makes the operation fail if the
|
||||
// object's ETag matches the given value. This is useful for getting updates
|
||||
// only after the object has changed since the last request.
|
||||
func (c *UserinfoGetCall) IfNoneMatch(entityTag string) *UserinfoGetCall {
|
||||
c.ifNoneMatch_ = entityTag
|
||||
return c
|
||||
}
|
||||
|
||||
// Context sets the context to be used in this call's Do method.
|
||||
func (c *UserinfoGetCall) Context(ctx context.Context) *UserinfoGetCall {
|
||||
c.ctx_ = ctx
|
||||
return c
|
||||
}
|
||||
|
||||
// Header returns a http.Header that can be modified by the caller to add
|
||||
// headers to the request.
|
||||
func (c *UserinfoGetCall) Header() http.Header {
|
||||
if c.header_ == nil {
|
||||
c.header_ = make(http.Header)
|
||||
}
|
||||
return c.header_
|
||||
}
|
||||
|
||||
func (c *UserinfoGetCall) doRequest(alt string) (*http.Response, error) {
|
||||
reqHeaders := gensupport.SetHeaders(c.s.userAgent(), "", c.header_)
|
||||
if c.ifNoneMatch_ != "" {
|
||||
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
|
||||
}
|
||||
c.urlParams_.Set("alt", alt)
|
||||
c.urlParams_.Set("prettyPrint", "false")
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "oauth2/v2/userinfo")
|
||||
urls += "?" + c.urlParams_.Encode()
|
||||
req, err := http.NewRequest("GET", urls, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
c.s.logger.DebugContext(c.ctx_, "api request", "serviceName", apiName, "rpcName", "oauth2.userinfo.get", "request", internallog.HTTPRequest(req, nil))
|
||||
return gensupport.SendRequest(c.ctx_, c.s.client, req)
|
||||
}
|
||||
|
||||
// Do executes the "oauth2.userinfo.get" call.
|
||||
// Any non-2xx status code is an error. Response headers are in either
|
||||
// *Userinfo.ServerResponse.Header or (if a response was returned at all) in
|
||||
// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
|
||||
// whether the returned error was because http.StatusNotModified was returned.
|
||||
func (c *UserinfoGetCall) Do(opts ...googleapi.CallOption) (*Userinfo, error) {
|
||||
gensupport.SetOptions(c.urlParams_, opts...)
|
||||
res, err := c.doRequest("json")
|
||||
if res != nil && res.StatusCode == http.StatusNotModified {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
return nil, gensupport.WrapError(&googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, gensupport.WrapError(err)
|
||||
}
|
||||
ret := &Userinfo{
|
||||
ServerResponse: googleapi.ServerResponse{
|
||||
Header: res.Header,
|
||||
HTTPStatusCode: res.StatusCode,
|
||||
},
|
||||
}
|
||||
target := &ret
|
||||
b, err := gensupport.DecodeResponseBytes(target, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.s.logger.DebugContext(c.ctx_, "api response", "serviceName", apiName, "rpcName", "oauth2.userinfo.get", "response", internallog.HTTPResponse(res, b))
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type UserinfoV2MeGetCall struct {
|
||||
s *Service
|
||||
urlParams_ gensupport.URLParams
|
||||
ifNoneMatch_ string
|
||||
ctx_ context.Context
|
||||
header_ http.Header
|
||||
}
|
||||
|
||||
// Get:
|
||||
func (r *UserinfoV2MeService) Get() *UserinfoV2MeGetCall {
|
||||
c := &UserinfoV2MeGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved. See
|
||||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse for more
|
||||
// details.
|
||||
func (c *UserinfoV2MeGetCall) Fields(s ...googleapi.Field) *UserinfoV2MeGetCall {
|
||||
c.urlParams_.Set("fields", googleapi.CombineFields(s))
|
||||
return c
|
||||
}
|
||||
|
||||
// IfNoneMatch sets an optional parameter which makes the operation fail if the
|
||||
// object's ETag matches the given value. This is useful for getting updates
|
||||
// only after the object has changed since the last request.
|
||||
func (c *UserinfoV2MeGetCall) IfNoneMatch(entityTag string) *UserinfoV2MeGetCall {
|
||||
c.ifNoneMatch_ = entityTag
|
||||
return c
|
||||
}
|
||||
|
||||
// Context sets the context to be used in this call's Do method.
|
||||
func (c *UserinfoV2MeGetCall) Context(ctx context.Context) *UserinfoV2MeGetCall {
|
||||
c.ctx_ = ctx
|
||||
return c
|
||||
}
|
||||
|
||||
// Header returns a http.Header that can be modified by the caller to add
|
||||
// headers to the request.
|
||||
func (c *UserinfoV2MeGetCall) Header() http.Header {
|
||||
if c.header_ == nil {
|
||||
c.header_ = make(http.Header)
|
||||
}
|
||||
return c.header_
|
||||
}
|
||||
|
||||
func (c *UserinfoV2MeGetCall) doRequest(alt string) (*http.Response, error) {
|
||||
reqHeaders := gensupport.SetHeaders(c.s.userAgent(), "", c.header_)
|
||||
if c.ifNoneMatch_ != "" {
|
||||
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
|
||||
}
|
||||
c.urlParams_.Set("alt", alt)
|
||||
c.urlParams_.Set("prettyPrint", "false")
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "userinfo/v2/me")
|
||||
urls += "?" + c.urlParams_.Encode()
|
||||
req, err := http.NewRequest("GET", urls, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
c.s.logger.DebugContext(c.ctx_, "api request", "serviceName", apiName, "rpcName", "oauth2.userinfo.v2.me.get", "request", internallog.HTTPRequest(req, nil))
|
||||
return gensupport.SendRequest(c.ctx_, c.s.client, req)
|
||||
}
|
||||
|
||||
// Do executes the "oauth2.userinfo.v2.me.get" call.
|
||||
// Any non-2xx status code is an error. Response headers are in either
|
||||
// *Userinfo.ServerResponse.Header or (if a response was returned at all) in
|
||||
// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
|
||||
// whether the returned error was because http.StatusNotModified was returned.
|
||||
func (c *UserinfoV2MeGetCall) Do(opts ...googleapi.CallOption) (*Userinfo, error) {
|
||||
gensupport.SetOptions(c.urlParams_, opts...)
|
||||
res, err := c.doRequest("json")
|
||||
if res != nil && res.StatusCode == http.StatusNotModified {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
return nil, gensupport.WrapError(&googleapi.Error{
|
||||
Code: res.StatusCode,
|
||||
Header: res.Header,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, gensupport.WrapError(err)
|
||||
}
|
||||
ret := &Userinfo{
|
||||
ServerResponse: googleapi.ServerResponse{
|
||||
Header: res.Header,
|
||||
HTTPStatusCode: res.StatusCode,
|
||||
},
|
||||
}
|
||||
target := &ret
|
||||
b, err := gensupport.DecodeResponseBytes(target, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.s.logger.DebugContext(c.ctx_, "api response", "serviceName", apiName, "rpcName", "oauth2.userinfo.v2.me.get", "response", internallog.HTTPResponse(res, b))
|
||||
return ret, nil
|
||||
}
|
||||
337
vendor/google.golang.org/api/option/internaloption/internaloption.go
generated
vendored
Normal file
337
vendor/google.golang.org/api/option/internaloption/internaloption.go
generated
vendored
Normal file
@@ -0,0 +1,337 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"log/slog"
|
||||
"maps"
|
||||
|
||||
"cloud.google.com/go/auth"
|
||||
"github.com/googleapis/gax-go/v2/internallog"
|
||||
"golang.org/x/oauth2/google"
|
||||
"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 overridden the default endpoint.
|
||||
//
|
||||
// Deprecated: WithDefaultEndpoint does not support setting the universe domain.
|
||||
// Use WithDefaultEndpointTemplate and WithDefaultUniverseDomain to compose the
|
||||
// default endpoint instead.
|
||||
func WithDefaultEndpoint(url string) option.ClientOption {
|
||||
return defaultEndpointOption(url)
|
||||
}
|
||||
|
||||
type defaultEndpointTemplateOption string
|
||||
|
||||
func (o defaultEndpointTemplateOption) Apply(settings *internal.DialSettings) {
|
||||
settings.DefaultEndpointTemplate = string(o)
|
||||
}
|
||||
|
||||
// WithDefaultEndpointTemplate provides a template for creating the endpoint
|
||||
// using a universe domain. See also WithDefaultUniverseDomain and
|
||||
// option.WithUniverseDomain. The placeholder UNIVERSE_DOMAIN should be used
|
||||
// instead of a concrete universe domain such as "googleapis.com".
|
||||
//
|
||||
// Example: WithDefaultEndpointTemplate("https://logging.UNIVERSE_DOMAIN/")
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
func WithDefaultEndpointTemplate(url string) option.ClientOption {
|
||||
return defaultEndpointTemplateOption(url)
|
||||
}
|
||||
|
||||
type defaultMTLSEndpointOption string
|
||||
|
||||
func (o defaultMTLSEndpointOption) Apply(settings *internal.DialSettings) {
|
||||
settings.DefaultMTLSEndpoint = string(o)
|
||||
}
|
||||
|
||||
// WithDefaultMTLSEndpoint is an option that indicates the default mTLS endpoint.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
func WithDefaultMTLSEndpoint(url string) option.ClientOption {
|
||||
return defaultMTLSEndpointOption(url)
|
||||
}
|
||||
|
||||
// SkipDialSettingsValidation bypasses validation on ClientOptions.
|
||||
//
|
||||
// It should only be used internally.
|
||||
func SkipDialSettingsValidation() option.ClientOption {
|
||||
return skipDialSettingsValidation{}
|
||||
}
|
||||
|
||||
type skipDialSettingsValidation struct{}
|
||||
|
||||
func (s skipDialSettingsValidation) Apply(settings *internal.DialSettings) {
|
||||
settings.SkipValidation = true
|
||||
}
|
||||
|
||||
// EnableDirectPath returns a ClientOption that overrides the default
|
||||
// attempt to use DirectPath.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
func EnableDirectPath(dp bool) option.ClientOption {
|
||||
return enableDirectPath(dp)
|
||||
}
|
||||
|
||||
type enableDirectPath bool
|
||||
|
||||
func (e enableDirectPath) Apply(o *internal.DialSettings) {
|
||||
o.EnableDirectPath = bool(e)
|
||||
}
|
||||
|
||||
// EnableDirectPathXds returns a ClientOption that overrides the default
|
||||
// DirectPath type. It is only valid when DirectPath is enabled.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
func EnableDirectPathXds() option.ClientOption {
|
||||
return enableDirectPathXds(true)
|
||||
}
|
||||
|
||||
type enableDirectPathXds bool
|
||||
|
||||
func (x enableDirectPathXds) Apply(o *internal.DialSettings) {
|
||||
o.EnableDirectPathXds = bool(x)
|
||||
}
|
||||
|
||||
// AllowNonDefaultServiceAccount returns a ClientOption that overrides the default
|
||||
// requirement for using the default service account for DirectPath.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
func AllowNonDefaultServiceAccount(nd bool) option.ClientOption {
|
||||
return allowNonDefaultServiceAccount(nd)
|
||||
}
|
||||
|
||||
type allowNonDefaultServiceAccount bool
|
||||
|
||||
func (a allowNonDefaultServiceAccount) Apply(o *internal.DialSettings) {
|
||||
o.AllowNonDefaultServiceAccount = bool(a)
|
||||
}
|
||||
|
||||
// WithDefaultAudience returns a ClientOption that specifies a default audience
|
||||
// to be used as the audience field ("aud") for the JWT token authentication.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
func WithDefaultAudience(audience string) option.ClientOption {
|
||||
return withDefaultAudience(audience)
|
||||
}
|
||||
|
||||
type withDefaultAudience string
|
||||
|
||||
func (w withDefaultAudience) Apply(o *internal.DialSettings) {
|
||||
o.DefaultAudience = string(w)
|
||||
}
|
||||
|
||||
// WithDefaultScopes returns a ClientOption that overrides the default OAuth2
|
||||
// scopes to be used for a service.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
func WithDefaultScopes(scope ...string) option.ClientOption {
|
||||
return withDefaultScopes(scope)
|
||||
}
|
||||
|
||||
type withDefaultScopes []string
|
||||
|
||||
func (w withDefaultScopes) Apply(o *internal.DialSettings) {
|
||||
o.DefaultScopes = make([]string, len(w))
|
||||
copy(o.DefaultScopes, w)
|
||||
}
|
||||
|
||||
// WithTelemetryAttributes returns a ClientOption that specifies a map of
|
||||
// telemetry attributes to be added to all telemetry signals, such as tracing
|
||||
// and metrics, for purposes including representing the static identity of the
|
||||
// client (e.g., service name, version). These attributes are expected to be
|
||||
// consistent across all signals to enable cross-signal correlation.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
func WithTelemetryAttributes(attrs map[string]string) option.ClientOption {
|
||||
return withTelemetryAttributes(attrs)
|
||||
}
|
||||
|
||||
type withTelemetryAttributes map[string]string
|
||||
|
||||
func (w withTelemetryAttributes) Apply(o *internal.DialSettings) {
|
||||
o.TelemetryAttributes = maps.Clone(w)
|
||||
}
|
||||
|
||||
// WithDefaultUniverseDomain returns a ClientOption that sets the default universe domain.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
//
|
||||
// This is similar to the public WithUniverse, but allows us to determine whether the user has
|
||||
// overridden the default universe.
|
||||
func WithDefaultUniverseDomain(ud string) option.ClientOption {
|
||||
return withDefaultUniverseDomain(ud)
|
||||
}
|
||||
|
||||
type withDefaultUniverseDomain string
|
||||
|
||||
func (w withDefaultUniverseDomain) Apply(o *internal.DialSettings) {
|
||||
o.DefaultUniverseDomain = string(w)
|
||||
}
|
||||
|
||||
// EnableJwtWithScope returns a ClientOption that specifies if scope can be used
|
||||
// with self-signed JWT.
|
||||
//
|
||||
// EnableJwtWithScope is ignored when option.WithUniverseDomain is set
|
||||
// to a value other than the Google Default Universe (GDU) of "googleapis.com".
|
||||
// For non-GDU domains, token exchange is impossible and services must
|
||||
// support self-signed JWTs with scopes.
|
||||
func EnableJwtWithScope() option.ClientOption {
|
||||
return enableJwtWithScope(true)
|
||||
}
|
||||
|
||||
type enableJwtWithScope bool
|
||||
|
||||
func (w enableJwtWithScope) Apply(o *internal.DialSettings) {
|
||||
o.EnableJwtWithScope = bool(w)
|
||||
}
|
||||
|
||||
// AllowHardBoundTokens returns a ClientOption that allows libraries to request a hard-bound token.
|
||||
// Obtaining hard-bound tokens requires the connection to be established using either Application
|
||||
// Layer Transport Security (ALTS) or mutual TLS (mTLS) with S2A. For more information on ALTS,
|
||||
// see: https://cloud.google.com/docs/security/encryption-in-transit/application-layer-transport-security
|
||||
//
|
||||
// The AllowHardBoundTokens option accepts the following values (or a combination thereof):
|
||||
//
|
||||
// - "MTLS_S2A": Allows obtaining hard-bound tokens when the connection uses mutual TLS with S2A.
|
||||
// - "ALTS": Allows obtaining hard-bound tokens when the connection uses ALTS.
|
||||
//
|
||||
// For example, to allow obtaining hard-bound tokens with either MTLS_S2A or ALTS, you would
|
||||
// provide both values (e.g., {"MTLS_S2A","ALTS"}). If no value is provided, hard-bound tokens
|
||||
// will not be requested.
|
||||
//
|
||||
// It should only be used internally by generated clients.
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
func AllowHardBoundTokens(protocol ...string) option.ClientOption {
|
||||
return allowHardBoundTokens(protocol)
|
||||
}
|
||||
|
||||
type allowHardBoundTokens []string
|
||||
|
||||
func (a allowHardBoundTokens) Apply(o *internal.DialSettings) {
|
||||
o.AllowHardBoundTokens = make([]string, len(a))
|
||||
copy(o.AllowHardBoundTokens, a)
|
||||
}
|
||||
|
||||
// WithCredentials returns a client option to specify credentials which will be used to authenticate API calls.
|
||||
// This credential takes precedence over all other credential options.
|
||||
func WithCredentials(creds *google.Credentials) option.ClientOption {
|
||||
return (*withCreds)(creds)
|
||||
}
|
||||
|
||||
type withCreds google.Credentials
|
||||
|
||||
func (w *withCreds) Apply(o *internal.DialSettings) {
|
||||
o.InternalCredentials = (*google.Credentials)(w)
|
||||
}
|
||||
|
||||
// EnableNewAuthLibrary returns a ClientOption that specifies if libraries in this
|
||||
// module to delegate auth to our new library. This option will be removed in
|
||||
// the future once all clients have been moved to the new auth layer.
|
||||
func EnableNewAuthLibrary() option.ClientOption {
|
||||
return enableNewAuthLibrary(true)
|
||||
}
|
||||
|
||||
type enableNewAuthLibrary bool
|
||||
|
||||
func (w enableNewAuthLibrary) Apply(o *internal.DialSettings) {
|
||||
o.EnableNewAuthLibrary = bool(w)
|
||||
}
|
||||
|
||||
// EnableAsyncRefreshDryRun returns a ClientOption that specifies if libraries in this
|
||||
// module should asynchronously refresh auth token in parallel to sync refresh.
|
||||
//
|
||||
// This option can be used to determine whether refreshing the token asymnchronously
|
||||
// prior to its actual expiry works without any issues in a particular environment.
|
||||
//
|
||||
// errHandler function will be called when there is an error while refreshing
|
||||
// the token asynchronously.
|
||||
//
|
||||
// This is an EXPERIMENTAL option and will be removed in the future.
|
||||
// TODO(b/372244283): Remove after b/358175516 has been fixed
|
||||
func EnableAsyncRefreshDryRun(errHandler func()) option.ClientOption {
|
||||
return enableAsyncRefreshDryRun{
|
||||
errHandler: errHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/372244283): Remove after b/358175516 has been fixed
|
||||
type enableAsyncRefreshDryRun struct {
|
||||
errHandler func()
|
||||
}
|
||||
|
||||
// TODO(b/372244283): Remove after b/358175516 has been fixed
|
||||
func (w enableAsyncRefreshDryRun) Apply(o *internal.DialSettings) {
|
||||
o.EnableAsyncRefreshDryRun = w.errHandler
|
||||
}
|
||||
|
||||
// EmbeddableAdapter is a no-op option.ClientOption that allow libraries to
|
||||
// create their own client options by embedding this type into their own
|
||||
// client-specific option wrapper. See example for usage.
|
||||
type EmbeddableAdapter struct{}
|
||||
|
||||
func (*EmbeddableAdapter) Apply(_ *internal.DialSettings) {}
|
||||
|
||||
// GetLogger is a helper for client libraries to extract the [slog.Logger] from
|
||||
// the provided options or return a default logger if one is not found.
|
||||
//
|
||||
// It should only be used internally by generated clients. This is an EXPERIMENTAL API
|
||||
// and may be changed or removed in the future.
|
||||
func GetLogger(opts []option.ClientOption) *slog.Logger {
|
||||
var ds internal.DialSettings
|
||||
for _, opt := range opts {
|
||||
opt.Apply(&ds)
|
||||
}
|
||||
return internallog.New(ds.Logger)
|
||||
}
|
||||
|
||||
// AuthCreds returns [cloud.google.com/go/auth.Credentials] using the following
|
||||
// options provided via [option.ClientOption], including legacy oauth2/google
|
||||
// options, in this order:
|
||||
//
|
||||
// - [option.WithoutAuthentication]
|
||||
// - [option.Credentials]
|
||||
// - [WithCredentials] (internal use only)
|
||||
// - [option.WithCredentials]
|
||||
// - [option.WithTokenSource]
|
||||
//
|
||||
// If there are no applicable credentials options, then it passes the
|
||||
// following options to [cloud.google.com/go/auth/credentials.DetectDefault] and
|
||||
// returns the result:
|
||||
//
|
||||
// - [option.WithAudiences]
|
||||
// - [option.WithAuthCredentialsFile]
|
||||
// - [option.WithCredentialsFile]
|
||||
// - [option.WithAuthCredentialsJSON]
|
||||
// - [option.WithCredentialsJSON]
|
||||
// - [option.WithScopes]
|
||||
// - [WithDefaultScopes] (internal use only)
|
||||
// - [EnableJwtWithScope] (internal use only)
|
||||
//
|
||||
// This function should only be used internally by generated clients. This is an
|
||||
// EXPERIMENTAL API and may be changed or removed in the future.
|
||||
func AuthCreds(ctx context.Context, opts []option.ClientOption) (*auth.Credentials, error) {
|
||||
var ds internal.DialSettings
|
||||
for _, opt := range opts {
|
||||
opt.Apply(&ds)
|
||||
}
|
||||
return internal.AuthCreds(ctx, &ds)
|
||||
}
|
||||
537
vendor/google.golang.org/api/option/option.go
generated
vendored
Normal file
537
vendor/google.golang.org/api/option/option.go
generated
vendored
Normal file
@@ -0,0 +1,537 @@
|
||||
// Copyright 2017 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package option contains options for Google API clients.
|
||||
package option
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"cloud.google.com/go/auth"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/internal"
|
||||
"google.golang.org/api/internal/credentialstype"
|
||||
"google.golang.org/api/internal/impersonate"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// CredentialsType specifies the type of JSON credentials being provided
|
||||
// to a loading function such as [WithAuthCredentialsFile] or
|
||||
// [WithAuthCredentialsJSON].
|
||||
type CredentialsType = credentialstype.CredType
|
||||
|
||||
const (
|
||||
// ServiceAccount represents a service account file type.
|
||||
ServiceAccount = credentialstype.ServiceAccount
|
||||
// AuthorizedUser represents an authorized user credentials file type.
|
||||
AuthorizedUser = credentialstype.AuthorizedUser
|
||||
// ImpersonatedServiceAccount represents an impersonated service account file type.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// This credential type does not validate the credential configuration. A security
|
||||
// risk occurs when a credential configuration configured with malicious urls
|
||||
// is used.
|
||||
// You should validate credential configurations provided by untrusted sources.
|
||||
// See [Security requirements when using credential configurations from an external
|
||||
// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||
// for more details.
|
||||
ImpersonatedServiceAccount = credentialstype.ImpersonatedServiceAccount
|
||||
// ExternalAccount represents an external account file type.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// This credential type does not validate the credential configuration. A security
|
||||
// risk occurs when a credential configuration configured with malicious urls
|
||||
// is used.
|
||||
// You should validate credential configurations provided by untrusted sources.
|
||||
// See [Security requirements when using credential configurations from an external
|
||||
// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||
// for more details.
|
||||
ExternalAccount = credentialstype.ExternalAccount
|
||||
)
|
||||
|
||||
// A ClientOption is an option for a Google API client.
|
||||
type ClientOption interface {
|
||||
Apply(*internal.DialSettings)
|
||||
}
|
||||
|
||||
// WithTokenSource returns a ClientOption that specifies an OAuth2 token
|
||||
// source to be used as the basis for authentication.
|
||||
func WithTokenSource(s oauth2.TokenSource) ClientOption {
|
||||
return withTokenSource{s}
|
||||
}
|
||||
|
||||
type withTokenSource struct{ ts oauth2.TokenSource }
|
||||
|
||||
func (w withTokenSource) Apply(o *internal.DialSettings) {
|
||||
o.TokenSource = w.ts
|
||||
}
|
||||
|
||||
type withCredFile string
|
||||
|
||||
func (w withCredFile) Apply(o *internal.DialSettings) {
|
||||
o.CredentialsFile = string(w)
|
||||
}
|
||||
|
||||
// WithCredentialsFile returns a ClientOption that authenticates
|
||||
// API calls with the given service account or refresh token JSON
|
||||
// credentials file.
|
||||
//
|
||||
// Deprecated: This function is being deprecated because of a potential security risk.
|
||||
//
|
||||
// This function does not validate the credential configuration. The security
|
||||
// risk occurs when a credential configuration is accepted from a source that
|
||||
// is not under your control and used without validation on your side.
|
||||
//
|
||||
// If you know that you will be loading credential configurations of a
|
||||
// specific type, it is recommended to use a credential-type-specific
|
||||
// option function.
|
||||
// This will ensure that an unexpected credential type with potential for
|
||||
// malicious intent is not loaded unintentionally. You might still have to do
|
||||
// validation for certain credential types. Please follow the recommendation
|
||||
// for that function. For example, if you want to load only service accounts,
|
||||
// you can use [WithAuthCredentialsFile] with [ServiceAccount]:
|
||||
//
|
||||
// option.WithAuthCredentialsFile(option.ServiceAccount, "/path/to/file.json")
|
||||
//
|
||||
// If you are loading your credential configuration from an untrusted source and have
|
||||
// not mitigated the risks (e.g. by validating the configuration yourself), make
|
||||
// these changes as soon as possible to prevent security risks to your environment.
|
||||
//
|
||||
// Regardless of the function used, it is always your responsibility to validate
|
||||
// configurations received from external sources.
|
||||
func WithCredentialsFile(filename string) ClientOption {
|
||||
return withCredFile(filename)
|
||||
}
|
||||
|
||||
// WithAuthCredentialsFile returns a ClientOption that authenticates API calls
|
||||
// with the given JSON credentials file and credential type.
|
||||
//
|
||||
// Important: If you accept a credential configuration (credential
|
||||
// JSON/File/Stream) from an external source for authentication to Google
|
||||
// Cloud Platform, you must validate it before providing it to any Google
|
||||
// API or library. Providing an unvalidated credential configuration to
|
||||
// Google APIs can compromise the security of your systems and data. For
|
||||
// more information, refer to [Validate credential configurations from
|
||||
// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
||||
func WithAuthCredentialsFile(credType CredentialsType, filename string) ClientOption {
|
||||
return withAuthCredentialsFile{
|
||||
credsType: credType,
|
||||
filename: filename,
|
||||
}
|
||||
}
|
||||
|
||||
type withAuthCredentialsFile struct {
|
||||
credsType CredentialsType
|
||||
filename string
|
||||
}
|
||||
|
||||
func (w withAuthCredentialsFile) Apply(o *internal.DialSettings) {
|
||||
o.AuthCredentialsFile = w.filename
|
||||
o.AuthCredentialsType = w.credsType
|
||||
}
|
||||
|
||||
// WithServiceAccountFile returns a ClientOption that uses a Google service
|
||||
// account credentials file to authenticate.
|
||||
//
|
||||
// Important: If you accept a credential configuration (credential
|
||||
// JSON/File/Stream) from an external source for authentication to Google
|
||||
// Cloud Platform, you must validate it before providing it to any Google
|
||||
// API or library. Providing an unvalidated credential configuration to
|
||||
// Google APIs can compromise the security of your systems and data. For
|
||||
// more information, refer to [Validate credential configurations from
|
||||
// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
||||
//
|
||||
// Deprecated: Use WithAuthCredentialsFile instead.
|
||||
func WithServiceAccountFile(filename string) ClientOption {
|
||||
return WithAuthCredentialsFile(ServiceAccount, filename)
|
||||
}
|
||||
|
||||
// WithCredentialsJSON returns a ClientOption that authenticates
|
||||
// API calls with the given service account or refresh token JSON
|
||||
// credentials.
|
||||
//
|
||||
// Deprecated: This function is being deprecated because of a potential security risk.
|
||||
//
|
||||
// This function does not validate the credential configuration. The security
|
||||
// risk occurs when a credential configuration is accepted from a source that
|
||||
// is not under your control and used without validation on your side.
|
||||
//
|
||||
// If you know that you will be loading credential configurations of a
|
||||
// specific type, it is recommended to use a credential-type-specific
|
||||
// option function.
|
||||
// This will ensure that an unexpected credential type with potential for
|
||||
// malicious intent is not loaded unintentionally. You might still have to do
|
||||
// validation for certain credential types. Please follow the recommendation
|
||||
// for that function. For example, if you want to load only service accounts,
|
||||
// you can use [WithAuthCredentialsJSON] with [ServiceAccount]:
|
||||
//
|
||||
// option.WithAuthCredentialsJSON(option.ServiceAccount, json)
|
||||
//
|
||||
// If you are loading your credential configuration from an untrusted source and have
|
||||
// not mitigated the risks (e.g. by validating the configuration yourself), make
|
||||
// these changes as soon as possible to prevent security risks to your environment.
|
||||
//
|
||||
// Regardless of the function used, it is always your responsibility to validate
|
||||
// configurations received from external sources.
|
||||
func WithCredentialsJSON(p []byte) ClientOption {
|
||||
return withCredentialsJSON(p)
|
||||
}
|
||||
|
||||
type withCredentialsJSON []byte
|
||||
|
||||
func (w withCredentialsJSON) Apply(o *internal.DialSettings) {
|
||||
o.CredentialsJSON = make([]byte, len(w))
|
||||
copy(o.CredentialsJSON, w)
|
||||
}
|
||||
|
||||
// WithAuthCredentialsJSON returns a ClientOption that authenticates API calls
|
||||
// with the given JSON credentials and credential type.
|
||||
//
|
||||
// Important: If you accept a credential configuration (credential
|
||||
// JSON/File/Stream) from an external source for authentication to Google
|
||||
// Cloud Platform, you must validate it before providing it to any Google
|
||||
// API or library. Providing an unvalidated credential configuration to
|
||||
// Google APIs can compromise the security of your systems and data. For
|
||||
// more information, refer to [Validate credential configurations from
|
||||
// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
||||
func WithAuthCredentialsJSON(credType CredentialsType, json []byte) ClientOption {
|
||||
return withAuthCredentialsJSON{
|
||||
credsType: credType,
|
||||
json: json,
|
||||
}
|
||||
}
|
||||
|
||||
type withAuthCredentialsJSON struct {
|
||||
credsType CredentialsType
|
||||
json []byte
|
||||
}
|
||||
|
||||
func (w withAuthCredentialsJSON) Apply(o *internal.DialSettings) {
|
||||
o.AuthCredentialsJSON = w.json
|
||||
o.AuthCredentialsType = w.credsType
|
||||
}
|
||||
|
||||
// WithEndpoint returns a ClientOption that overrides the default endpoint
|
||||
// to be used for a service. Please note that by default Google APIs only
|
||||
// accept HTTPS traffic.
|
||||
//
|
||||
// For a gRPC client, the port number is typically included in the endpoint.
|
||||
// Example: "us-central1-speech.googleapis.com:443".
|
||||
//
|
||||
// For a REST client, the port number is typically not included. Example:
|
||||
// "https://speech.googleapis.com".
|
||||
func WithEndpoint(url string) ClientOption {
|
||||
return withEndpoint(url)
|
||||
}
|
||||
|
||||
type withEndpoint string
|
||||
|
||||
func (w withEndpoint) Apply(o *internal.DialSettings) {
|
||||
o.Endpoint = string(w)
|
||||
}
|
||||
|
||||
// WithScopes returns a ClientOption that overrides the default OAuth2 scopes
|
||||
// to be used for a service.
|
||||
//
|
||||
// If both WithScopes and WithTokenSource are used, scope settings from the
|
||||
// token source will be used instead.
|
||||
func WithScopes(scope ...string) ClientOption {
|
||||
return withScopes(scope)
|
||||
}
|
||||
|
||||
type withScopes []string
|
||||
|
||||
func (w withScopes) Apply(o *internal.DialSettings) {
|
||||
o.Scopes = make([]string, len(w))
|
||||
copy(o.Scopes, w)
|
||||
}
|
||||
|
||||
// WithUserAgent returns a ClientOption that sets the User-Agent. This option
|
||||
// is incompatible with the [WithHTTPClient] option. If you wish to provide a
|
||||
// custom client you will need to add this header via RoundTripper middleware.
|
||||
func WithUserAgent(ua string) ClientOption {
|
||||
return withUA(ua)
|
||||
}
|
||||
|
||||
type withUA string
|
||||
|
||||
func (w withUA) Apply(o *internal.DialSettings) { o.UserAgent = string(w) }
|
||||
|
||||
// WithHTTPClient returns a ClientOption that specifies the HTTP client to use
|
||||
// as the basis of communications. This option may only be used with services
|
||||
// that support HTTP as their communication transport. When used, the
|
||||
// WithHTTPClient option takes precedent over all other supplied options.
|
||||
func WithHTTPClient(client *http.Client) ClientOption {
|
||||
return withHTTPClient{client}
|
||||
}
|
||||
|
||||
type withHTTPClient struct{ client *http.Client }
|
||||
|
||||
func (w withHTTPClient) Apply(o *internal.DialSettings) {
|
||||
o.HTTPClient = w.client
|
||||
}
|
||||
|
||||
// WithGRPCConn returns a ClientOption that specifies the gRPC client
|
||||
// connection to use as the basis of communications. This option may only be
|
||||
// used with services that support gRPC as their communication transport. When
|
||||
// used, the WithGRPCConn option takes precedent over all other supplied
|
||||
// options.
|
||||
func WithGRPCConn(conn *grpc.ClientConn) ClientOption {
|
||||
return withGRPCConn{conn}
|
||||
}
|
||||
|
||||
type withGRPCConn struct{ conn *grpc.ClientConn }
|
||||
|
||||
func (w withGRPCConn) Apply(o *internal.DialSettings) {
|
||||
o.GRPCConn = w.conn
|
||||
}
|
||||
|
||||
// WithGRPCDialOption returns a ClientOption that appends a new grpc.DialOption
|
||||
// to an underlying gRPC dial. It does not work with WithGRPCConn.
|
||||
func WithGRPCDialOption(opt grpc.DialOption) ClientOption {
|
||||
return withGRPCDialOption{opt}
|
||||
}
|
||||
|
||||
type withGRPCDialOption struct{ opt grpc.DialOption }
|
||||
|
||||
func (w withGRPCDialOption) Apply(o *internal.DialSettings) {
|
||||
o.GRPCDialOpts = append(o.GRPCDialOpts, w.opt)
|
||||
}
|
||||
|
||||
// WithGRPCConnectionPool returns a ClientOption that creates a pool of gRPC
|
||||
// connections that requests will be balanced between.
|
||||
func WithGRPCConnectionPool(size int) ClientOption {
|
||||
return withGRPCConnectionPool(size)
|
||||
}
|
||||
|
||||
type withGRPCConnectionPool int
|
||||
|
||||
func (w withGRPCConnectionPool) Apply(o *internal.DialSettings) {
|
||||
o.GRPCConnPoolSize = int(w)
|
||||
}
|
||||
|
||||
// WithAPIKey returns a ClientOption that specifies an API key to be used
|
||||
// as the basis for authentication.
|
||||
//
|
||||
// API Keys can only be used for JSON-over-HTTP APIs, including those under
|
||||
// the import path google.golang.org/api/....
|
||||
func WithAPIKey(apiKey string) ClientOption {
|
||||
return withAPIKey(apiKey)
|
||||
}
|
||||
|
||||
type withAPIKey string
|
||||
|
||||
func (w withAPIKey) Apply(o *internal.DialSettings) { o.APIKey = string(w) }
|
||||
|
||||
// WithAudiences returns a ClientOption that specifies an audience to be used
|
||||
// as the audience field ("aud") for the JWT token authentication.
|
||||
func WithAudiences(audience ...string) ClientOption {
|
||||
return withAudiences(audience)
|
||||
}
|
||||
|
||||
type withAudiences []string
|
||||
|
||||
func (w withAudiences) Apply(o *internal.DialSettings) {
|
||||
o.Audiences = make([]string, len(w))
|
||||
copy(o.Audiences, w)
|
||||
}
|
||||
|
||||
// WithoutAuthentication returns a ClientOption that specifies that no
|
||||
// authentication should be used. It is suitable only for testing and for
|
||||
// accessing public resources, like public Google Cloud Storage buckets.
|
||||
// It is an error to provide both WithoutAuthentication and any of WithAPIKey,
|
||||
// WithTokenSource, WithCredentialsFile or WithServiceAccountFile.
|
||||
func WithoutAuthentication() ClientOption {
|
||||
return withoutAuthentication{}
|
||||
}
|
||||
|
||||
type withoutAuthentication struct{}
|
||||
|
||||
func (w withoutAuthentication) Apply(o *internal.DialSettings) { o.NoAuth = true }
|
||||
|
||||
// WithQuotaProject returns a ClientOption that specifies the project used
|
||||
// for quota and billing purposes.
|
||||
//
|
||||
// For more information please read:
|
||||
// https://cloud.google.com/apis/docs/system-parameters
|
||||
func WithQuotaProject(quotaProject string) ClientOption {
|
||||
return withQuotaProject(quotaProject)
|
||||
}
|
||||
|
||||
type withQuotaProject string
|
||||
|
||||
func (w withQuotaProject) Apply(o *internal.DialSettings) {
|
||||
o.QuotaProject = string(w)
|
||||
}
|
||||
|
||||
// WithRequestReason returns a ClientOption that specifies a reason for
|
||||
// making the request, which is intended to be recorded in audit logging.
|
||||
// An example reason would be a support-case ticket number.
|
||||
//
|
||||
// For more information please read:
|
||||
// https://cloud.google.com/apis/docs/system-parameters
|
||||
func WithRequestReason(requestReason string) ClientOption {
|
||||
return withRequestReason(requestReason)
|
||||
}
|
||||
|
||||
type withRequestReason string
|
||||
|
||||
func (w withRequestReason) Apply(o *internal.DialSettings) {
|
||||
o.RequestReason = string(w)
|
||||
}
|
||||
|
||||
// WithTelemetryDisabled returns a ClientOption that disables default telemetry (OpenCensus)
|
||||
// settings on gRPC and HTTP clients.
|
||||
// An example reason would be to bind custom telemetry that overrides the defaults.
|
||||
func WithTelemetryDisabled() ClientOption {
|
||||
return withTelemetryDisabled{}
|
||||
}
|
||||
|
||||
type withTelemetryDisabled struct{}
|
||||
|
||||
func (w withTelemetryDisabled) Apply(o *internal.DialSettings) {
|
||||
o.TelemetryDisabled = true
|
||||
}
|
||||
|
||||
// ClientCertSource is a function that returns a TLS client certificate to be used
|
||||
// when opening TLS connections.
|
||||
//
|
||||
// It follows the same semantics as crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
type ClientCertSource = func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||
|
||||
// WithClientCertSource returns a ClientOption that specifies a
|
||||
// callback function for obtaining a TLS client certificate.
|
||||
//
|
||||
// This option is used for supporting mTLS authentication, where the
|
||||
// server validates the client certifcate when establishing a connection.
|
||||
//
|
||||
// The callback function will be invoked whenever the server requests a
|
||||
// certificate from the client. Implementations of the callback function
|
||||
// should try to ensure that a valid certificate can be repeatedly returned
|
||||
// on demand for the entire life cycle of the transport client. If a nil
|
||||
// Certificate is returned (i.e. no Certificate can be obtained), an error
|
||||
// should be returned.
|
||||
//
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
func WithClientCertSource(s ClientCertSource) ClientOption {
|
||||
return withClientCertSource{s}
|
||||
}
|
||||
|
||||
type withClientCertSource struct{ s ClientCertSource }
|
||||
|
||||
func (w withClientCertSource) Apply(o *internal.DialSettings) {
|
||||
o.ClientCertSource = w.s
|
||||
}
|
||||
|
||||
// ImpersonateCredentials returns a ClientOption that will impersonate the
|
||||
// target service account.
|
||||
//
|
||||
// In order to impersonate the target service account
|
||||
// the base service account must have the Service Account Token Creator role,
|
||||
// roles/iam.serviceAccountTokenCreator, on the target service account.
|
||||
// See https://cloud.google.com/iam/docs/understanding-service-accounts.
|
||||
//
|
||||
// Optionally, delegates can be used during impersonation if the base service
|
||||
// account lacks the token creator role on the target. When using delegates,
|
||||
// each service account must be granted roles/iam.serviceAccountTokenCreator
|
||||
// on the next service account in the chain.
|
||||
//
|
||||
// For example, if a base service account of SA1 is trying to impersonate target
|
||||
// service account SA2 while using delegate service accounts DSA1 and DSA2,
|
||||
// the following must be true:
|
||||
//
|
||||
// 1. Base service account SA1 has roles/iam.serviceAccountTokenCreator on
|
||||
// DSA1.
|
||||
// 2. DSA1 has roles/iam.serviceAccountTokenCreator on DSA2.
|
||||
// 3. DSA2 has roles/iam.serviceAccountTokenCreator on target SA2.
|
||||
//
|
||||
// The resulting impersonated credential will either have the default scopes of
|
||||
// the client being instantiating or the scopes from WithScopes if provided.
|
||||
// Scopes are required for creating impersonated credentials, so if this option
|
||||
// is used while not using a NewClient/NewService function, WithScopes must also
|
||||
// be explicitly passed in as well.
|
||||
//
|
||||
// If the base credential is an authorized user and not a service account, or if
|
||||
// the option WithQuotaProject is set, the target service account must have a
|
||||
// role that grants the serviceusage.services.use permission such as
|
||||
// roles/serviceusage.serviceUsageConsumer.
|
||||
//
|
||||
// This is an EXPERIMENTAL API and may be changed or removed in the future.
|
||||
//
|
||||
// Deprecated: This option has been replaced by `impersonate` package:
|
||||
// `google.golang.org/api/impersonate`. Please use the `impersonate` package
|
||||
// instead with the WithTokenSource option.
|
||||
func ImpersonateCredentials(target string, delegates ...string) ClientOption {
|
||||
return impersonateServiceAccount{
|
||||
target: target,
|
||||
delegates: delegates,
|
||||
}
|
||||
}
|
||||
|
||||
type impersonateServiceAccount struct {
|
||||
target string
|
||||
delegates []string
|
||||
}
|
||||
|
||||
func (i impersonateServiceAccount) Apply(o *internal.DialSettings) {
|
||||
o.ImpersonationConfig = &impersonate.Config{
|
||||
Target: i.target,
|
||||
}
|
||||
o.ImpersonationConfig.Delegates = make([]string, len(i.delegates))
|
||||
copy(o.ImpersonationConfig.Delegates, i.delegates)
|
||||
}
|
||||
|
||||
type withCreds google.Credentials
|
||||
|
||||
func (w *withCreds) Apply(o *internal.DialSettings) {
|
||||
o.Credentials = (*google.Credentials)(w)
|
||||
}
|
||||
|
||||
// WithCredentials returns a ClientOption that authenticates API calls.
|
||||
func WithCredentials(creds *google.Credentials) ClientOption {
|
||||
return (*withCreds)(creds)
|
||||
}
|
||||
|
||||
// WithAuthCredentials returns a ClientOption that specifies an
|
||||
// [cloud.google.com/go/auth.Credentials] to be used as the basis for
|
||||
// authentication.
|
||||
func WithAuthCredentials(creds *auth.Credentials) ClientOption {
|
||||
return withAuthCredentials{creds}
|
||||
}
|
||||
|
||||
type withAuthCredentials struct{ creds *auth.Credentials }
|
||||
|
||||
func (w withAuthCredentials) Apply(o *internal.DialSettings) {
|
||||
o.AuthCredentials = w.creds
|
||||
}
|
||||
|
||||
// WithUniverseDomain returns a ClientOption that sets the universe domain.
|
||||
func WithUniverseDomain(ud string) ClientOption {
|
||||
return withUniverseDomain(ud)
|
||||
}
|
||||
|
||||
type withUniverseDomain string
|
||||
|
||||
func (w withUniverseDomain) Apply(o *internal.DialSettings) {
|
||||
o.UniverseDomain = string(w)
|
||||
}
|
||||
|
||||
// WithLogger returns a ClientOption that sets the logger used throughout the
|
||||
// client library call stack. If this option is provided it takes precedence
|
||||
// over the value set in GOOGLE_SDK_GO_LOGGING_LEVEL. Specifying this option
|
||||
// enables logging at the provided logger's configured level.
|
||||
func WithLogger(l *slog.Logger) ClientOption {
|
||||
return withLogger{l}
|
||||
}
|
||||
|
||||
type withLogger struct{ l *slog.Logger }
|
||||
|
||||
func (w withLogger) Apply(o *internal.DialSettings) {
|
||||
o.Logger = w.l
|
||||
}
|
||||
324
vendor/google.golang.org/api/transport/http/dial.go
generated
vendored
Normal file
324
vendor/google.golang.org/api/transport/http/dial.go
generated
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
// Copyright 2015 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package http supports network connections to HTTP servers.
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/auth"
|
||||
"cloud.google.com/go/auth/credentials"
|
||||
"cloud.google.com/go/auth/httptransport"
|
||||
"cloud.google.com/go/auth/oauth2adapt"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/googleapi/transport"
|
||||
"google.golang.org/api/internal"
|
||||
"google.golang.org/api/internal/cert"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
// NewClient returns an HTTP client for use communicating with a Google cloud
|
||||
// service, configured with the given ClientOptions. It also returns the endpoint
|
||||
// for the service as specified in the options.
|
||||
func NewClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) {
|
||||
settings, err := newSettings(opts)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
clientCertSource, dialTLSContext, endpoint, err := internal.GetHTTPTransportConfigAndEndpoint(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, endpoint, nil
|
||||
}
|
||||
|
||||
if settings.IsNewAuthLibraryEnabled() {
|
||||
client, err := newClientNewAuth(ctx, nil, settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return client, endpoint, nil
|
||||
}
|
||||
trans, err := newTransport(ctx, defaultBaseTransport(ctx, clientCertSource, dialTLSContext), settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &http.Client{Transport: trans}, endpoint, nil
|
||||
}
|
||||
|
||||
// newClientNewAuth is an adapter to call new auth library.
|
||||
func newClientNewAuth(ctx context.Context, base http.RoundTripper, ds *internal.DialSettings) (*http.Client, error) {
|
||||
// honor options if set
|
||||
var creds *auth.Credentials
|
||||
if ds.InternalCredentials != nil {
|
||||
creds = oauth2adapt.AuthCredentialsFromOauth2Credentials(ds.InternalCredentials)
|
||||
} else if ds.Credentials != nil {
|
||||
creds = oauth2adapt.AuthCredentialsFromOauth2Credentials(ds.Credentials)
|
||||
} else if ds.AuthCredentials != nil {
|
||||
creds = ds.AuthCredentials
|
||||
} else if ds.TokenSource != nil {
|
||||
credOpts := &auth.CredentialsOptions{
|
||||
TokenProvider: oauth2adapt.TokenProviderFromTokenSource(ds.TokenSource),
|
||||
}
|
||||
if ds.QuotaProject != "" {
|
||||
credOpts.QuotaProjectIDProvider = auth.CredentialsPropertyFunc(func(ctx context.Context) (string, error) {
|
||||
return ds.QuotaProject, nil
|
||||
})
|
||||
}
|
||||
creds = auth.NewCredentials(credOpts)
|
||||
}
|
||||
|
||||
var skipValidation bool
|
||||
// If our clients explicitly setup the credential skip validation as it is
|
||||
// assumed correct
|
||||
if ds.SkipValidation || ds.InternalCredentials != nil {
|
||||
skipValidation = true
|
||||
}
|
||||
|
||||
// Defaults for older clients that don't set this value yet
|
||||
defaultEndpointTemplate := ds.DefaultEndpointTemplate
|
||||
if defaultEndpointTemplate == "" {
|
||||
defaultEndpointTemplate = ds.DefaultEndpoint
|
||||
}
|
||||
|
||||
var aud string
|
||||
if len(ds.Audiences) > 0 {
|
||||
aud = ds.Audiences[0]
|
||||
}
|
||||
headers := http.Header{}
|
||||
if ds.QuotaProject != "" {
|
||||
headers.Set("X-goog-user-project", ds.QuotaProject)
|
||||
}
|
||||
if ds.RequestReason != "" {
|
||||
headers.Set("X-goog-request-reason", ds.RequestReason)
|
||||
}
|
||||
if ds.UserAgent != "" {
|
||||
headers.Set("User-Agent", ds.UserAgent)
|
||||
}
|
||||
credsJSON, _ := ds.GetAuthCredentialsJSON()
|
||||
credsFile, _ := ds.GetAuthCredentialsFile()
|
||||
client, err := httptransport.NewClient(&httptransport.Options{
|
||||
DisableTelemetry: ds.TelemetryDisabled,
|
||||
DisableAuthentication: ds.NoAuth,
|
||||
Headers: headers,
|
||||
Endpoint: ds.Endpoint,
|
||||
APIKey: ds.APIKey,
|
||||
Credentials: creds,
|
||||
ClientCertProvider: ds.ClientCertSource,
|
||||
BaseRoundTripper: base,
|
||||
DetectOpts: &credentials.DetectOptions{
|
||||
Scopes: ds.Scopes,
|
||||
Audience: aud,
|
||||
CredentialsFile: credsFile,
|
||||
CredentialsJSON: credsJSON,
|
||||
Logger: ds.Logger,
|
||||
},
|
||||
InternalOptions: &httptransport.InternalOptions{
|
||||
EnableJWTWithScope: ds.EnableJwtWithScope,
|
||||
DefaultAudience: ds.DefaultAudience,
|
||||
DefaultEndpointTemplate: defaultEndpointTemplate,
|
||||
DefaultMTLSEndpoint: ds.DefaultMTLSEndpoint,
|
||||
DefaultScopes: ds.DefaultScopes,
|
||||
SkipValidation: skipValidation,
|
||||
TelemetryAttributes: ds.TelemetryAttributes,
|
||||
},
|
||||
UniverseDomain: ds.UniverseDomain,
|
||||
Logger: ds.Logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewTransport creates an http.RoundTripper for use communicating with a Google
|
||||
// cloud service, configured with the given ClientOptions. Its RoundTrip method delegates to base.
|
||||
func NewTransport(ctx context.Context, base http.RoundTripper, opts ...option.ClientOption) (http.RoundTripper, error) {
|
||||
settings, err := newSettings(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if settings.HTTPClient != nil {
|
||||
return nil, errors.New("transport/http: WithHTTPClient passed to NewTransport")
|
||||
}
|
||||
if settings.IsNewAuthLibraryEnabled() {
|
||||
client, err := newClientNewAuth(ctx, base, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.Transport, nil
|
||||
}
|
||||
return newTransport(ctx, base, settings)
|
||||
}
|
||||
|
||||
func newTransport(ctx context.Context, base http.RoundTripper, settings *internal.DialSettings) (http.RoundTripper, error) {
|
||||
paramTransport := ¶meterTransport{
|
||||
base: base,
|
||||
userAgent: settings.UserAgent,
|
||||
requestReason: settings.RequestReason,
|
||||
}
|
||||
var trans http.RoundTripper = paramTransport
|
||||
trans = addOpenTelemetryTransport(trans, settings)
|
||||
switch {
|
||||
case settings.NoAuth:
|
||||
// Do nothing.
|
||||
case settings.APIKey != "":
|
||||
paramTransport.quotaProject = internal.GetQuotaProject(nil, settings.QuotaProject)
|
||||
trans = &transport.APIKey{
|
||||
Transport: trans,
|
||||
Key: settings.APIKey,
|
||||
}
|
||||
default:
|
||||
creds, err := internal.Creds(ctx, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paramTransport.quotaProject = internal.GetQuotaProject(creds, settings.QuotaProject)
|
||||
ts := creds.TokenSource
|
||||
if settings.ImpersonationConfig == nil && settings.TokenSource != nil {
|
||||
ts = settings.TokenSource
|
||||
}
|
||||
trans = &oauth2.Transport{
|
||||
Base: trans,
|
||||
Source: ts,
|
||||
}
|
||||
}
|
||||
return trans, nil
|
||||
}
|
||||
|
||||
func newSettings(opts []option.ClientOption) (*internal.DialSettings, error) {
|
||||
var o internal.DialSettings
|
||||
for _, opt := range opts {
|
||||
opt.Apply(&o)
|
||||
}
|
||||
if err := o.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if o.GRPCConn != nil {
|
||||
return nil, errors.New("unsupported gRPC connection specified")
|
||||
}
|
||||
return &o, nil
|
||||
}
|
||||
|
||||
type parameterTransport struct {
|
||||
userAgent string
|
||||
quotaProject string
|
||||
requestReason string
|
||||
|
||||
base http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *parameterTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt := t.base
|
||||
if rt == nil {
|
||||
return nil, errors.New("transport: no Transport specified")
|
||||
}
|
||||
newReq := *req
|
||||
newReq.Header = make(http.Header)
|
||||
for k, vv := range req.Header {
|
||||
newReq.Header[k] = vv
|
||||
}
|
||||
if t.userAgent != "" {
|
||||
// TODO(cbro): append to existing User-Agent header?
|
||||
newReq.Header.Set("User-Agent", t.userAgent)
|
||||
}
|
||||
|
||||
// Attach system parameters into the header
|
||||
if t.quotaProject != "" {
|
||||
newReq.Header.Set("X-Goog-User-Project", t.quotaProject)
|
||||
}
|
||||
if t.requestReason != "" {
|
||||
newReq.Header.Set("X-Goog-Request-Reason", t.requestReason)
|
||||
}
|
||||
|
||||
return rt.RoundTrip(&newReq)
|
||||
}
|
||||
|
||||
// defaultBaseTransport returns the base HTTP transport. It uses a default
|
||||
// transport, taking most defaults from http.DefaultTransport.
|
||||
// If TLSCertificate is available, set TLSClientConfig as well.
|
||||
func defaultBaseTransport(ctx context.Context, clientCertSource cert.Source, dialTLSContext func(context.Context, string, string) (net.Conn, error)) http.RoundTripper {
|
||||
// Copy http.DefaultTransport except for MaxIdleConnsPerHost setting,
|
||||
// which is increased due to reported performance issues under load in the
|
||||
// GCS client. Transport.Clone is only available in Go 1.13 and up.
|
||||
trans := clonedTransport(http.DefaultTransport)
|
||||
if trans == nil {
|
||||
trans = fallbackBaseTransport()
|
||||
}
|
||||
trans.MaxIdleConnsPerHost = 100
|
||||
|
||||
if clientCertSource != nil {
|
||||
trans.TLSClientConfig = &tls.Config{
|
||||
GetClientCertificate: clientCertSource,
|
||||
}
|
||||
}
|
||||
if dialTLSContext != nil {
|
||||
// If DialTLSContext is set, TLSClientConfig wil be ignored
|
||||
trans.DialTLSContext = dialTLSContext
|
||||
}
|
||||
|
||||
configureHTTP2(trans)
|
||||
|
||||
return trans
|
||||
}
|
||||
|
||||
// configureHTTP2 configures the ReadIdleTimeout HTTP/2 option for the
|
||||
// transport. This allows broken idle connections to be pruned more quickly,
|
||||
// preventing the client from attempting to re-use connections that will no
|
||||
// longer work.
|
||||
func configureHTTP2(trans *http.Transport) {
|
||||
http2Trans, err := http2.ConfigureTransports(trans)
|
||||
if err == nil {
|
||||
http2Trans.ReadIdleTimeout = time.Second * 31
|
||||
}
|
||||
}
|
||||
|
||||
// fallbackBaseTransport is used in <go1.13 as well as in the rare case if
|
||||
// http.DefaultTransport has been reassigned something that's not a
|
||||
// *http.Transport.
|
||||
func fallbackBaseTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func addOpenTelemetryTransport(trans http.RoundTripper, settings *internal.DialSettings) http.RoundTripper {
|
||||
if settings.TelemetryDisabled {
|
||||
return trans
|
||||
}
|
||||
return otelhttp.NewTransport(trans)
|
||||
}
|
||||
|
||||
// clonedTransport returns the given RoundTripper as a cloned *http.Transport.
|
||||
// It returns nil if the RoundTripper can't be cloned or coerced to
|
||||
// *http.Transport.
|
||||
func clonedTransport(rt http.RoundTripper) *http.Transport {
|
||||
t, ok := rt.(*http.Transport)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return t.Clone()
|
||||
}
|
||||
Reference in New Issue
Block a user