chore: migrate to gitea
Some checks failed
golangci-lint / lint (push) Failing after 1m30s
Test / test (push) Failing after 2m17s

This commit is contained in:
2026-01-27 00:40:46 +01:00
parent 1e05874160
commit f8df24c29d
3169 changed files with 1216434 additions and 1587 deletions

11
vendor/google.golang.org/api/AUTHORS generated vendored Normal file
View 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
View 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
View 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
View 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

View 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
View 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
View 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"
}

View 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
}

View 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
}

View 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
View 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
}

View 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
View 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,
}
}

View 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
}

View 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

View 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
}

View 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
}

View 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")
}

View 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
}

View 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
}

View 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)
}

View 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)
}
}

View 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
}

View 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)
}

View 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
View 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
View 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
}
}

View 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.

View 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
}

View 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)
}

View 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
View 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
View 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
View 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
View 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
View 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
}

View 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
View 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
View 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 := &parameterTransport{
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()
}

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,336 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v4.24.4
// source: google/rpc/code.proto
package code
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// The canonical error codes for gRPC APIs.
//
// Sometimes multiple error codes may apply. Services should return
// the most specific error code that applies. For example, prefer
// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply.
// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`.
type Code int32
const (
// Not an error; returned on success.
//
// HTTP Mapping: 200 OK
Code_OK Code = 0
// The operation was cancelled, typically by the caller.
//
// HTTP Mapping: 499 Client Closed Request
Code_CANCELLED Code = 1
// Unknown error. For example, this error may be returned when
// a `Status` value received from another address space belongs to
// an error space that is not known in this address space. Also
// errors raised by APIs that do not return enough error information
// may be converted to this error.
//
// HTTP Mapping: 500 Internal Server Error
Code_UNKNOWN Code = 2
// The client specified an invalid argument. Note that this differs
// from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments
// that are problematic regardless of the state of the system
// (e.g., a malformed file name).
//
// HTTP Mapping: 400 Bad Request
Code_INVALID_ARGUMENT Code = 3
// The deadline expired before the operation could complete. For operations
// that change the state of the system, this error may be returned
// even if the operation has completed successfully. For example, a
// successful response from a server could have been delayed long
// enough for the deadline to expire.
//
// HTTP Mapping: 504 Gateway Timeout
Code_DEADLINE_EXCEEDED Code = 4
// Some requested entity (e.g., file or directory) was not found.
//
// Note to server developers: if a request is denied for an entire class
// of users, such as gradual feature rollout or undocumented allowlist,
// `NOT_FOUND` may be used. If a request is denied for some users within
// a class of users, such as user-based access control, `PERMISSION_DENIED`
// must be used.
//
// HTTP Mapping: 404 Not Found
Code_NOT_FOUND Code = 5
// The entity that a client attempted to create (e.g., file or directory)
// already exists.
//
// HTTP Mapping: 409 Conflict
Code_ALREADY_EXISTS Code = 6
// The caller does not have permission to execute the specified
// operation. `PERMISSION_DENIED` must not be used for rejections
// caused by exhausting some resource (use `RESOURCE_EXHAUSTED`
// instead for those errors). `PERMISSION_DENIED` must not be
// used if the caller can not be identified (use `UNAUTHENTICATED`
// instead for those errors). This error code does not imply the
// request is valid or the requested entity exists or satisfies
// other pre-conditions.
//
// HTTP Mapping: 403 Forbidden
Code_PERMISSION_DENIED Code = 7
// The request does not have valid authentication credentials for the
// operation.
//
// HTTP Mapping: 401 Unauthorized
Code_UNAUTHENTICATED Code = 16
// Some resource has been exhausted, perhaps a per-user quota, or
// perhaps the entire file system is out of space.
//
// HTTP Mapping: 429 Too Many Requests
Code_RESOURCE_EXHAUSTED Code = 8
// The operation was rejected because the system is not in a state
// required for the operation's execution. For example, the directory
// to be deleted is non-empty, an rmdir operation is applied to
// a non-directory, etc.
//
// Service implementors can use the following guidelines to decide
// between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`:
//
// (a) Use `UNAVAILABLE` if the client can retry just the failing call.
// (b) Use `ABORTED` if the client should retry at a higher level. For
// example, when a client-specified test-and-set fails, indicating the
// client should restart a read-modify-write sequence.
// (c) Use `FAILED_PRECONDITION` if the client should not retry until
// the system state has been explicitly fixed. For example, if an "rmdir"
// fails because the directory is non-empty, `FAILED_PRECONDITION`
// should be returned since the client should not retry unless
// the files are deleted from the directory.
//
// HTTP Mapping: 400 Bad Request
Code_FAILED_PRECONDITION Code = 9
// The operation was aborted, typically due to a concurrency issue such as
// a sequencer check failure or transaction abort.
//
// See the guidelines above for deciding between `FAILED_PRECONDITION`,
// `ABORTED`, and `UNAVAILABLE`.
//
// HTTP Mapping: 409 Conflict
Code_ABORTED Code = 10
// The operation was attempted past the valid range. E.g., seeking or
// reading past end-of-file.
//
// Unlike `INVALID_ARGUMENT`, this error indicates a problem that may
// be fixed if the system state changes. For example, a 32-bit file
// system will generate `INVALID_ARGUMENT` if asked to read at an
// offset that is not in the range [0,2^32-1], but it will generate
// `OUT_OF_RANGE` if asked to read from an offset past the current
// file size.
//
// There is a fair bit of overlap between `FAILED_PRECONDITION` and
// `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific
// error) when it applies so that callers who are iterating through
// a space can easily look for an `OUT_OF_RANGE` error to detect when
// they are done.
//
// HTTP Mapping: 400 Bad Request
Code_OUT_OF_RANGE Code = 11
// The operation is not implemented or is not supported/enabled in this
// service.
//
// HTTP Mapping: 501 Not Implemented
Code_UNIMPLEMENTED Code = 12
// Internal errors. This means that some invariants expected by the
// underlying system have been broken. This error code is reserved
// for serious errors.
//
// HTTP Mapping: 500 Internal Server Error
Code_INTERNAL Code = 13
// The service is currently unavailable. This is most likely a
// transient condition, which can be corrected by retrying with
// a backoff. Note that it is not always safe to retry
// non-idempotent operations.
//
// See the guidelines above for deciding between `FAILED_PRECONDITION`,
// `ABORTED`, and `UNAVAILABLE`.
//
// HTTP Mapping: 503 Service Unavailable
Code_UNAVAILABLE Code = 14
// Unrecoverable data loss or corruption.
//
// HTTP Mapping: 500 Internal Server Error
Code_DATA_LOSS Code = 15
)
// Enum value maps for Code.
var (
Code_name = map[int32]string{
0: "OK",
1: "CANCELLED",
2: "UNKNOWN",
3: "INVALID_ARGUMENT",
4: "DEADLINE_EXCEEDED",
5: "NOT_FOUND",
6: "ALREADY_EXISTS",
7: "PERMISSION_DENIED",
16: "UNAUTHENTICATED",
8: "RESOURCE_EXHAUSTED",
9: "FAILED_PRECONDITION",
10: "ABORTED",
11: "OUT_OF_RANGE",
12: "UNIMPLEMENTED",
13: "INTERNAL",
14: "UNAVAILABLE",
15: "DATA_LOSS",
}
Code_value = map[string]int32{
"OK": 0,
"CANCELLED": 1,
"UNKNOWN": 2,
"INVALID_ARGUMENT": 3,
"DEADLINE_EXCEEDED": 4,
"NOT_FOUND": 5,
"ALREADY_EXISTS": 6,
"PERMISSION_DENIED": 7,
"UNAUTHENTICATED": 16,
"RESOURCE_EXHAUSTED": 8,
"FAILED_PRECONDITION": 9,
"ABORTED": 10,
"OUT_OF_RANGE": 11,
"UNIMPLEMENTED": 12,
"INTERNAL": 13,
"UNAVAILABLE": 14,
"DATA_LOSS": 15,
}
)
func (x Code) Enum() *Code {
p := new(Code)
*p = x
return p
}
func (x Code) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Code) Descriptor() protoreflect.EnumDescriptor {
return file_google_rpc_code_proto_enumTypes[0].Descriptor()
}
func (Code) Type() protoreflect.EnumType {
return &file_google_rpc_code_proto_enumTypes[0]
}
func (x Code) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Code.Descriptor instead.
func (Code) EnumDescriptor() ([]byte, []int) {
return file_google_rpc_code_proto_rawDescGZIP(), []int{0}
}
var File_google_rpc_code_proto protoreflect.FileDescriptor
var file_google_rpc_code_proto_rawDesc = []byte{
0x0a, 0x15, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x64,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x72, 0x70, 0x63, 0x2a, 0xb7, 0x02, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02,
0x4f, 0x4b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45,
0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x02,
0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x52, 0x47, 0x55,
0x4d, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49,
0x4e, 0x45, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x04, 0x12, 0x0d, 0x0a,
0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e,
0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x10, 0x06,
0x12, 0x15, 0x0a, 0x11, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44,
0x45, 0x4e, 0x49, 0x45, 0x44, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x4e, 0x41, 0x55, 0x54,
0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x10, 0x12, 0x16, 0x0a, 0x12,
0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x48, 0x41, 0x55, 0x53, 0x54,
0x45, 0x44, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x50,
0x52, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x09, 0x12, 0x0b, 0x0a,
0x07, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x55,
0x54, 0x5f, 0x4f, 0x46, 0x5f, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x0b, 0x12, 0x11, 0x0a, 0x0d,
0x55, 0x4e, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x0c, 0x12,
0x0c, 0x0a, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x0d, 0x12, 0x0f, 0x0a,
0x0b, 0x55, 0x4e, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0e, 0x12, 0x0d,
0x0a, 0x09, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x4c, 0x4f, 0x53, 0x53, 0x10, 0x0f, 0x42, 0x58, 0x0a,
0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x42,
0x09, 0x43, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f,
0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61,
0x70, 0x69, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x3b, 0x63, 0x6f, 0x64,
0x65, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_google_rpc_code_proto_rawDescOnce sync.Once
file_google_rpc_code_proto_rawDescData = file_google_rpc_code_proto_rawDesc
)
func file_google_rpc_code_proto_rawDescGZIP() []byte {
file_google_rpc_code_proto_rawDescOnce.Do(func() {
file_google_rpc_code_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_rpc_code_proto_rawDescData)
})
return file_google_rpc_code_proto_rawDescData
}
var file_google_rpc_code_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_google_rpc_code_proto_goTypes = []interface{}{
(Code)(0), // 0: google.rpc.Code
}
var file_google_rpc_code_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_google_rpc_code_proto_init() }
func file_google_rpc_code_proto_init() {
if File_google_rpc_code_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_google_rpc_code_proto_rawDesc,
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_google_rpc_code_proto_goTypes,
DependencyIndexes: file_google_rpc_code_proto_depIdxs,
EnumInfos: file_google_rpc_code_proto_enumTypes,
}.Build()
File_google_rpc_code_proto = out.File
file_google_rpc_code_proto_rawDesc = nil
file_google_rpc_code_proto_goTypes = nil
file_google_rpc_code_proto_depIdxs = nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v4.24.4
// source: google/rpc/status.proto
package status
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// The `Status` type defines a logical error model that is suitable for
// different programming environments, including REST APIs and RPC APIs. It is
// used by [gRPC](https://github.com/grpc). Each `Status` message contains
// three pieces of data: error code, error message, and error details.
//
// You can find out more about this error model and how to work with it in the
// [API Design Guide](https://cloud.google.com/apis/design/errors).
type Status struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The status code, which should be an enum value of
// [google.rpc.Code][google.rpc.Code].
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
// A developer-facing error message, which should be in English. Any
// user-facing error message should be localized and sent in the
// [google.rpc.Status.details][google.rpc.Status.details] field, or localized
// by the client.
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
// A list of messages that carry the error details. There is a common set of
// message types for APIs to use.
Details []*anypb.Any `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty"`
}
func (x *Status) Reset() {
*x = Status{}
if protoimpl.UnsafeEnabled {
mi := &file_google_rpc_status_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Status) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Status) ProtoMessage() {}
func (x *Status) ProtoReflect() protoreflect.Message {
mi := &file_google_rpc_status_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Status.ProtoReflect.Descriptor instead.
func (*Status) Descriptor() ([]byte, []int) {
return file_google_rpc_status_proto_rawDescGZIP(), []int{0}
}
func (x *Status) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *Status) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *Status) GetDetails() []*anypb.Any {
if x != nil {
return x.Details
}
return nil
}
var File_google_rpc_status_proto protoreflect.FileDescriptor
var file_google_rpc_status_proto_rawDesc = []byte{
0x0a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x72, 0x70, 0x63, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x22, 0x66, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f,
0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18,
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x61, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x42, 0x0b, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3b, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_google_rpc_status_proto_rawDescOnce sync.Once
file_google_rpc_status_proto_rawDescData = file_google_rpc_status_proto_rawDesc
)
func file_google_rpc_status_proto_rawDescGZIP() []byte {
file_google_rpc_status_proto_rawDescOnce.Do(func() {
file_google_rpc_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_rpc_status_proto_rawDescData)
})
return file_google_rpc_status_proto_rawDescData
}
var file_google_rpc_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_google_rpc_status_proto_goTypes = []interface{}{
(*Status)(nil), // 0: google.rpc.Status
(*anypb.Any)(nil), // 1: google.protobuf.Any
}
var file_google_rpc_status_proto_depIdxs = []int32{
1, // 0: google.rpc.Status.details:type_name -> google.protobuf.Any
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_google_rpc_status_proto_init() }
func file_google_rpc_status_proto_init() {
if File_google_rpc_status_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_google_rpc_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Status); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_google_rpc_status_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_google_rpc_status_proto_goTypes,
DependencyIndexes: file_google_rpc_status_proto_depIdxs,
MessageInfos: file_google_rpc_status_proto_msgTypes,
}.Build()
File_google_rpc_status_proto = out.File
file_google_rpc_status_proto_rawDesc = nil
file_google_rpc_status_proto_goTypes = nil
file_google_rpc_status_proto_depIdxs = nil
}

1
vendor/google.golang.org/grpc/AUTHORS generated vendored Normal file
View File

@@ -0,0 +1 @@
Google Inc.

3
vendor/google.golang.org/grpc/CODE-OF-CONDUCT.md generated vendored Normal file
View File

@@ -0,0 +1,3 @@
## Community Code of Conduct
gRPC follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

159
vendor/google.golang.org/grpc/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,159 @@
# How to contribute
We welcome your patches and contributions to gRPC! Please read the gRPC
organization's [governance
rules](https://github.com/grpc/grpc-community/blob/master/governance.md) before
proceeding.
If you are new to GitHub, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/)
## Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License
Agreement](https://identity.linuxfoundation.org/projects/cncf). When you create
your first PR, a link will be added as a comment that contains the steps needed
to complete this process.
## Getting Started
A great way to start is by searching through our open issues. [Unassigned issues
labeled as "help
wanted"](https://github.com/grpc/grpc-go/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20label%3A%22Status%3A%20Help%20Wanted%22%20no%3Aassignee)
are especially nice for first-time contributors, as they should be well-defined
problems that already have agreed-upon solutions.
## Code Style
We follow [Google's published Go style
guide](https://google.github.io/styleguide/go/). Note that there are three
primary documents that make up this style guide; please follow them as closely
as possible. If a reviewer recommends something that contradicts those
guidelines, there may be valid reasons to do so, but it should be rare.
## Guidelines for Pull Requests
Please read the following carefully to ensure your contributions can be merged
smoothly and quickly.
### PR Contents
- Create **small PRs** that are narrowly focused on **addressing a single
concern**. We often receive PRs that attempt to fix several things at the same
time, and if one part of the PR has a problem, that will hold up the entire
PR.
- If your change does not address an **open issue** with an **agreed
resolution**, consider opening an issue and discussing it first. If you are
suggesting a behavioral or API change, consider starting with a [gRFC
proposal](https://github.com/grpc/proposal). Many new features that are not
bug fixes will require cross-language agreement.
- If you want to fix **formatting or style**, consider whether your changes are
an obvious improvement or might be considered a personal preference. If a
style change is based on preference, it likely will not be accepted. If it
corrects widely agreed-upon anti-patterns, then please do create a PR and
explain the benefits of the change.
- For correcting **misspellings**, please be aware that we use some terms that
are sometimes flagged by spell checkers. As an example, "if an only if" is
often written as "iff". Please do not make spelling correction changes unless
you are certain they are misspellings.
- **All tests need to be passing** before your change can be merged. We
recommend you run tests locally before creating your PR to catch breakages
early on:
- `./scripts/vet.sh` to catch vet errors.
- `go test -cpu 1,4 -timeout 7m ./...` to run the tests.
- `go test -race -cpu 1,4 -timeout 7m ./...` to run tests in race mode.
Note that we have a multi-module repo, so `go test` commands may need to be
run from the root of each module in order to cause all tests to run.
*Alternatively*, you may find it easier to push your changes to your fork on
GitHub, which will trigger a GitHub Actions run that you can use to verify
everything is passing.
- Note that there are two GitHub actions checks that need not be green:
1. We test the freshness of the generated proto code we maintain via the
`vet-proto` check. If the source proto files are updated, but our repo is
not updated, an optional checker will fail. This will be fixed by our team
in a separate PR and will not prevent the merge of your PR.
2. We run a checker that will fail if there is any change in dependencies of
an exported package via the `dependencies` check. If new dependencies are
added that are not appropriate, we may not accept your PR (see below).
- If you are adding a **new file**, make sure it has the **copyright message**
template at the top as a comment. You can copy the message from an existing
file and update the year.
- The grpc package should only depend on standard Go packages and a small number
of exceptions. **If your contribution introduces new dependencies**, you will
need a discussion with gRPC-Go maintainers.
### PR Descriptions
- **PR titles** should start with the name of the component being addressed, or
the type of change. Examples: transport, client, server, round_robin, xds,
cleanup, deps.
- Read and follow the **guidelines for PR titles and descriptions** here:
https://google.github.io/eng-practices/review/developer/cl-descriptions.html
*particularly* the sections "First Line" and "Body is Informative".
Note: your PR description will be used as the git commit message in a
squash-and-merge if your PR is approved. We may make changes to this as
necessary.
- **Does this PR relate to an open issue?** On the first line, please use the
tag `Fixes #<issue>` to ensure the issue is closed when the PR is merged. Or
use `Updates #<issue>` if the PR is related to an open issue, but does not fix
it. Consider filing an issue if one does not already exist.
- PR descriptions *must* conclude with **release notes** as follows:
```
RELEASE NOTES:
* <component>: <summary>
```
This need not match the PR title.
The summary must:
* be something that gRPC users will understand.
* clearly explain the feature being added, the issue being fixed, or the
behavior being changed, etc. If fixing a bug, be clear about how the bug
can be triggered by an end-user.
* begin with a capital letter and use complete sentences.
* be as short as possible to describe the change being made.
If a PR is *not* end-user visible -- e.g. a cleanup, testing change, or
GitHub-related, use `RELEASE NOTES: n/a`.
### PR Process
- Please **self-review** your code changes before sending your PR. This will
prevent simple, obvious errors from causing delays.
- Maintain a **clean commit history** and use **meaningful commit messages**.
PRs with messy commit histories are difficult to review and won't be merged.
Before sending your PR, ensure your changes are based on top of the latest
`upstream/master` commits, and avoid rebasing in the middle of a code review.
You should **never use `git push -f`** unless absolutely necessary during a
review, as it can interfere with GitHub's tracking of comments.
- Unless your PR is trivial, you should **expect reviewer comments** that you
will need to address before merging. We'll label the PR as `Status: Requires
Reporter Clarification` if we expect you to respond to these comments in a
timely manner. If the PR remains inactive for 6 days, it will be marked as
`stale`, and we will automatically close it after 7 days if we don't hear back
from you. Please feel free to ping issues or bugs if you do not get a response
within a week.

1
vendor/google.golang.org/grpc/GOVERNANCE.md generated vendored Normal file
View File

@@ -0,0 +1 @@
This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md).

202
vendor/google.golang.org/grpc/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

36
vendor/google.golang.org/grpc/MAINTAINERS.md generated vendored Normal file
View File

@@ -0,0 +1,36 @@
This page lists all active maintainers of this repository. If you were a
maintainer and would like to add your name to the Emeritus list, please send us a
PR.
See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
for governance guidelines and how to become a maintainer.
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
for general contribution guidelines.
## Maintainers (in alphabetical order)
- [arjan-bal](https://github.com/arjan-bal), Google LLC
- [arvindbr8](https://github.com/arvindbr8), Google LLC
- [atollena](https://github.com/atollena), Datadog, Inc.
- [dfawley](https://github.com/dfawley), Google LLC
- [easwars](https://github.com/easwars), Google LLC
- [gtcooke94](https://github.com/gtcooke94), Google LLC
## Emeritus Maintainers (in alphabetical order)
- [adelez](https://github.com/adelez)
- [aranjans](https://github.com/aranjans)
- [canguler](https://github.com/canguler)
- [cesarghali](https://github.com/cesarghali)
- [erm-g](https://github.com/erm-g)
- [iamqizhao](https://github.com/iamqizhao)
- [jeanbza](https://github.com/jeanbza)
- [jtattermusch](https://github.com/jtattermusch)
- [lyuxuan](https://github.com/lyuxuan)
- [makmukhi](https://github.com/makmukhi)
- [matt-kwong](https://github.com/matt-kwong)
- [menghanl](https://github.com/menghanl)
- [nicolasnoble](https://github.com/nicolasnoble)
- [purnesh42h](https://github.com/purnesh42h)
- [srini100](https://github.com/srini100)
- [yongni](https://github.com/yongni)
- [zasweq](https://github.com/zasweq)

49
vendor/google.golang.org/grpc/Makefile generated vendored Normal file
View File

@@ -0,0 +1,49 @@
all: vet test testrace
build:
go build google.golang.org/grpc/...
clean:
go clean -i google.golang.org/grpc/...
deps:
GO111MODULE=on go get -d -v google.golang.org/grpc/...
proto:
@ if ! which protoc > /dev/null; then \
echo "error: protoc not installed" >&2; \
exit 1; \
fi
go generate google.golang.org/grpc/...
test:
go test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
testsubmodule:
cd security/advancedtls && go test -cpu 1,4 -timeout 7m google.golang.org/grpc/security/advancedtls/...
cd security/authorization && go test -cpu 1,4 -timeout 7m google.golang.org/grpc/security/authorization/...
testrace:
go test -race -cpu 1,4 -timeout 7m google.golang.org/grpc/...
testdeps:
GO111MODULE=on go get -d -v -t google.golang.org/grpc/...
vet: vetdeps
./scripts/vet.sh
vetdeps:
./scripts/vet.sh -install
.PHONY: \
all \
build \
clean \
deps \
proto \
test \
testsubmodule \
testrace \
testdeps \
vet \
vetdeps

13
vendor/google.golang.org/grpc/NOTICE.txt generated vendored Normal file
View File

@@ -0,0 +1,13 @@
Copyright 2014 gRPC authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

108
vendor/google.golang.org/grpc/README.md generated vendored Normal file
View File

@@ -0,0 +1,108 @@
# gRPC-Go
[![GoDoc](https://pkg.go.dev/badge/google.golang.org/grpc)][API]
[![GoReportCard](https://goreportcard.com/badge/grpc/grpc-go)](https://goreportcard.com/report/github.com/grpc/grpc-go)
[![codecov](https://codecov.io/gh/grpc/grpc-go/graph/badge.svg)](https://codecov.io/gh/grpc/grpc-go)
The [Go][] implementation of [gRPC][]: A high performance, open source, general
RPC framework that puts mobile and HTTP/2 first. For more information see the
[Go gRPC docs][], or jump directly into the [quick start][].
## Prerequisites
- **[Go][]**: any one of the **two latest major** [releases][go-releases].
## Installation
Simply add the following import to your code, and then `go [build|run|test]`
will automatically fetch the necessary dependencies:
```go
import "google.golang.org/grpc"
```
> **Note:** If you are trying to access `grpc-go` from **China**, see the
> [FAQ](#FAQ) below.
## Learn more
- [Go gRPC docs][], which include a [quick start][] and [API
reference][API] among other resources
- [Low-level technical docs](Documentation) from this repository
- [Performance benchmark][]
- [Examples](examples)
- [Contribution guidelines](CONTRIBUTING.md)
## FAQ
### I/O Timeout Errors
The `golang.org` domain may be blocked from some countries. `go get` usually
produces an error like the following when this happens:
```console
$ go get -u google.golang.org/grpc
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
```
To build Go code, there are several options:
- Set up a VPN and access google.golang.org through that.
- With Go module support: it is possible to use the `replace` feature of `go
mod` to create aliases for golang.org packages. In your project's directory:
```sh
go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
go mod tidy
go mod vendor
go build -mod=vendor
```
Again, this will need to be done for all transitive dependencies hosted on
golang.org as well. For details, refer to [golang/go issue
#28652](https://github.com/golang/go/issues/28652).
### Compiling error, undefined: grpc.SupportPackageIsVersion
Please update to the latest version of gRPC-Go using
`go get google.golang.org/grpc`.
### How to turn on logging
The default logger is controlled by environment variables. Turn everything on
like this:
```console
$ export GRPC_GO_LOG_VERBOSITY_LEVEL=99
$ export GRPC_GO_LOG_SEVERITY_LEVEL=info
```
### The RPC failed with error `"code = Unavailable desc = transport is closing"`
This error means the connection the RPC is using was closed, and there are many
possible reasons, including:
1. mis-configured transport credentials, connection failed on handshaking
1. bytes disrupted, possibly by a proxy in between
1. server shutdown
1. Keepalive parameters caused connection shutdown, for example if you have
configured your server to terminate connections regularly to [trigger DNS
lookups](https://github.com/grpc/grpc-go/issues/3170#issuecomment-552517779).
If this is the case, you may want to increase your
[MaxConnectionAgeGrace](https://pkg.go.dev/google.golang.org/grpc/keepalive?tab=doc#ServerParameters),
to allow longer RPC calls to finish.
It can be tricky to debug this because the error happens on the client side but
the root cause of the connection being closed is on the server side. Turn on
logging on __both client and server__, and see if there are any transport
errors.
[API]: https://pkg.go.dev/google.golang.org/grpc
[Go]: https://golang.org
[Go module]: https://github.com/golang/go/wiki/Modules
[gRPC]: https://grpc.io
[Go gRPC docs]: https://grpc.io/docs/languages/go
[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5180705743044608
[quick start]: https://grpc.io/docs/languages/go/quickstart
[go-releases]: https://golang.org/doc/devel/release.html

3
vendor/google.golang.org/grpc/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Security Policy
For information on gRPC Security Policy and reporting potential security issues, please see [gRPC CVE Process](https://github.com/grpc/proposal/blob/master/P4-grpc-cve-process.md).

141
vendor/google.golang.org/grpc/attributes/attributes.go generated vendored Normal file
View File

@@ -0,0 +1,141 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package attributes defines a generic key/value store used in various gRPC
// components.
//
// # Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
// later release.
package attributes
import (
"fmt"
"strings"
)
// Attributes is an immutable struct for storing and retrieving generic
// key/value pairs. Keys must be hashable, and users should define their own
// types for keys. Values should not be modified after they are added to an
// Attributes or if they were received from one. If values implement 'Equal(o
// any) bool', it will be called by (*Attributes).Equal to determine whether
// two values with the same key should be considered equal.
type Attributes struct {
m map[any]any
}
// New returns a new Attributes containing the key/value pair.
func New(key, value any) *Attributes {
return &Attributes{m: map[any]any{key: value}}
}
// WithValue returns a new Attributes containing the previous keys and values
// and the new key/value pair. If the same key appears multiple times, the
// last value overwrites all previous values for that key. To remove an
// existing key, use a nil value. value should not be modified later.
func (a *Attributes) WithValue(key, value any) *Attributes {
if a == nil {
return New(key, value)
}
n := &Attributes{m: make(map[any]any, len(a.m)+1)}
for k, v := range a.m {
n.m[k] = v
}
n.m[key] = value
return n
}
// Value returns the value associated with these attributes for key, or nil if
// no value is associated with key. The returned value should not be modified.
func (a *Attributes) Value(key any) any {
if a == nil {
return nil
}
return a.m[key]
}
// Equal returns whether a and o are equivalent. If 'Equal(o any) bool' is
// implemented for a value in the attributes, it is called to determine if the
// value matches the one stored in the other attributes. If Equal is not
// implemented, standard equality is used to determine if the two values are
// equal. Note that some types (e.g. maps) aren't comparable by default, so
// they must be wrapped in a struct, or in an alias type, with Equal defined.
func (a *Attributes) Equal(o *Attributes) bool {
if a == nil && o == nil {
return true
}
if a == nil || o == nil {
return false
}
if len(a.m) != len(o.m) {
return false
}
for k, v := range a.m {
ov, ok := o.m[k]
if !ok {
// o missing element of a
return false
}
if eq, ok := v.(interface{ Equal(o any) bool }); ok {
if !eq.Equal(ov) {
return false
}
} else if v != ov {
// Fallback to a standard equality check if Value is unimplemented.
return false
}
}
return true
}
// String prints the attribute map. If any key or values throughout the map
// implement fmt.Stringer, it calls that method and appends.
func (a *Attributes) String() string {
var sb strings.Builder
sb.WriteString("{")
first := true
for k, v := range a.m {
if !first {
sb.WriteString(", ")
}
sb.WriteString(fmt.Sprintf("%q: %q ", str(k), str(v)))
first = false
}
sb.WriteString("}")
return sb.String()
}
func str(x any) (s string) {
if v, ok := x.(fmt.Stringer); ok {
return fmt.Sprint(v)
} else if v, ok := x.(string); ok {
return v
}
return fmt.Sprintf("<%p>", x)
}
// MarshalJSON helps implement the json.Marshaler interface, thereby rendering
// the Attributes correctly when printing (via pretty.JSON) structs containing
// Attributes as fields.
//
// Is it impossible to unmarshal attributes from a JSON representation and this
// method is meant only for debugging purposes.
func (a *Attributes) MarshalJSON() ([]byte, error) {
return []byte(a.String()), nil
}

61
vendor/google.golang.org/grpc/backoff.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// See internal/backoff package for the backoff implementation. This file is
// kept for the exported types and API backward compatibility.
package grpc
import (
"time"
"google.golang.org/grpc/backoff"
)
// DefaultBackoffConfig uses values specified for backoff in
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
//
// Deprecated: use ConnectParams instead. Will be supported throughout 1.x.
var DefaultBackoffConfig = BackoffConfig{
MaxDelay: 120 * time.Second,
}
// BackoffConfig defines the parameters for the default gRPC backoff strategy.
//
// Deprecated: use ConnectParams instead. Will be supported throughout 1.x.
type BackoffConfig struct {
// MaxDelay is the upper bound of backoff delay.
MaxDelay time.Duration
}
// ConnectParams defines the parameters for connecting and retrying. Users are
// encouraged to use this instead of the BackoffConfig type defined above. See
// here for more details:
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
//
// # Experimental
//
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
// later release.
type ConnectParams struct {
// Backoff specifies the configuration options for connection backoff.
Backoff backoff.Config
// MinConnectTimeout is the minimum amount of time we are willing to give a
// connection to complete.
MinConnectTimeout time.Duration
}

52
vendor/google.golang.org/grpc/backoff/backoff.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package backoff provides configuration options for backoff.
//
// More details can be found at:
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
//
// All APIs in this package are experimental.
package backoff
import "time"
// Config defines the configuration options for backoff.
type Config struct {
// BaseDelay is the amount of time to backoff after the first failure.
BaseDelay time.Duration
// Multiplier is the factor with which to multiply backoffs after a
// failed retry. Should ideally be greater than 1.
Multiplier float64
// Jitter is the factor with which backoffs are randomized.
Jitter float64
// MaxDelay is the upper bound of backoff delay.
MaxDelay time.Duration
}
// DefaultConfig is a backoff configuration with the default values specified
// at https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
//
// This should be useful for callers who want to configure backoff with
// non-default values only for a subset of the options.
var DefaultConfig = Config{
BaseDelay: 1.0 * time.Second,
Multiplier: 1.6,
Jitter: 0.2,
MaxDelay: 120 * time.Second,
}

394
vendor/google.golang.org/grpc/balancer/balancer.go generated vendored Normal file
View File

@@ -0,0 +1,394 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package balancer defines APIs for load balancing in gRPC.
// All APIs in this package are experimental.
package balancer
import (
"context"
"encoding/json"
"errors"
"net"
"strings"
"google.golang.org/grpc/channelz"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
estats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
)
var (
// m is a map from name to balancer builder.
m = make(map[string]Builder)
logger = grpclog.Component("balancer")
)
// Register registers the balancer builder to the balancer map. b.Name
// (lowercased) will be used as the name registered with this builder. If the
// Builder implements ConfigParser, ParseConfig will be called when new service
// configs are received by the resolver, and the result will be provided to the
// Balancer in UpdateClientConnState.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple Balancers are
// registered with the same name, the one registered last will take effect.
func Register(b Builder) {
name := strings.ToLower(b.Name())
if name != b.Name() {
// TODO: Skip the use of strings.ToLower() to index the map after v1.59
// is released to switch to case sensitive balancer registry. Also,
// remove this warning and update the docstrings for Register and Get.
logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon", b.Name())
}
m[name] = b
}
// unregisterForTesting deletes the balancer with the given name from the
// balancer map.
//
// This function is not thread-safe.
func unregisterForTesting(name string) {
delete(m, name)
}
func init() {
internal.BalancerUnregister = unregisterForTesting
internal.ConnectedAddress = connectedAddress
internal.SetConnectedAddress = setConnectedAddress
}
// Get returns the resolver builder registered with the given name.
// Note that the compare is done in a case-insensitive fashion.
// If no builder is register with the name, nil will be returned.
func Get(name string) Builder {
if strings.ToLower(name) != name {
// TODO: Skip the use of strings.ToLower() to index the map after v1.59
// is released to switch to case sensitive balancer registry. Also,
// remove this warning and update the docstrings for Register and Get.
logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon", name)
}
if b, ok := m[strings.ToLower(name)]; ok {
return b
}
return nil
}
// NewSubConnOptions contains options to create new SubConn.
type NewSubConnOptions struct {
// CredsBundle is the credentials bundle that will be used in the created
// SubConn. If it's nil, the original creds from grpc DialOptions will be
// used.
//
// Deprecated: Use the Attributes field in resolver.Address to pass
// arbitrary data to the credential handshaker.
CredsBundle credentials.Bundle
// HealthCheckEnabled indicates whether health check service should be
// enabled on this SubConn
HealthCheckEnabled bool
// StateListener is called when the state of the subconn changes. If nil,
// Balancer.UpdateSubConnState will be called instead. Will never be
// invoked until after Connect() is called on the SubConn created with
// these options.
StateListener func(SubConnState)
}
// State contains the balancer's state relevant to the gRPC ClientConn.
type State struct {
// State contains the connectivity state of the balancer, which is used to
// determine the state of the ClientConn.
ConnectivityState connectivity.State
// Picker is used to choose connections (SubConns) for RPCs.
Picker Picker
}
// ClientConn represents a gRPC ClientConn.
//
// This interface is to be implemented by gRPC. Users should not need a
// brand new implementation of this interface. For the situations like
// testing, the new implementation should embed this interface. This allows
// gRPC to add new methods to this interface.
//
// NOTICE: This interface is intended to be implemented by gRPC, or intercepted
// by custom load balancing polices. Users should not need their own complete
// implementation of this interface -- they should always delegate to a
// ClientConn passed to Builder.Build() by embedding it in their
// implementations. An embedded ClientConn must never be nil, or runtime panics
// will occur.
type ClientConn interface {
// NewSubConn is called by balancer to create a new SubConn.
// It doesn't block and wait for the connections to be established.
// Behaviors of the SubConn can be controlled by options.
//
// Deprecated: please be aware that in a future version, SubConns will only
// support one address per SubConn.
NewSubConn([]resolver.Address, NewSubConnOptions) (SubConn, error)
// RemoveSubConn removes the SubConn from ClientConn.
// The SubConn will be shutdown.
//
// Deprecated: use SubConn.Shutdown instead.
RemoveSubConn(SubConn)
// UpdateAddresses updates the addresses used in the passed in SubConn.
// gRPC checks if the currently connected address is still in the new list.
// If so, the connection will be kept. Else, the connection will be
// gracefully closed, and a new connection will be created.
//
// This may trigger a state transition for the SubConn.
//
// Deprecated: this method will be removed. Create new SubConns for new
// addresses instead.
UpdateAddresses(SubConn, []resolver.Address)
// UpdateState notifies gRPC that the balancer's internal state has
// changed.
//
// gRPC will update the connectivity state of the ClientConn, and will call
// Pick on the new Picker to pick new SubConns.
UpdateState(State)
// ResolveNow is called by balancer to notify gRPC to do a name resolving.
ResolveNow(resolver.ResolveNowOptions)
// Target returns the dial target for this ClientConn.
//
// Deprecated: Use the Target field in the BuildOptions instead.
Target() string
// MetricsRecorder provides the metrics recorder that balancers can use to
// record metrics. Balancer implementations which do not register metrics on
// metrics registry and record on them can ignore this method. The returned
// MetricsRecorder is guaranteed to never be nil.
MetricsRecorder() estats.MetricsRecorder
// EnforceClientConnEmbedding is included to force implementers to embed
// another implementation of this interface, allowing gRPC to add methods
// without breaking users.
internal.EnforceClientConnEmbedding
}
// BuildOptions contains additional information for Build.
type BuildOptions struct {
// DialCreds is the transport credentials to use when communicating with a
// remote load balancer server. Balancer implementations which do not
// communicate with a remote load balancer server can ignore this field.
DialCreds credentials.TransportCredentials
// CredsBundle is the credentials bundle to use when communicating with a
// remote load balancer server. Balancer implementations which do not
// communicate with a remote load balancer server can ignore this field.
CredsBundle credentials.Bundle
// Dialer is the custom dialer to use when communicating with a remote load
// balancer server. Balancer implementations which do not communicate with a
// remote load balancer server can ignore this field.
Dialer func(context.Context, string) (net.Conn, error)
// Authority is the server name to use as part of the authentication
// handshake when communicating with a remote load balancer server. Balancer
// implementations which do not communicate with a remote load balancer
// server can ignore this field.
Authority string
// ChannelzParent is the parent ClientConn's channelz channel.
ChannelzParent channelz.Identifier
// CustomUserAgent is the custom user agent set on the parent ClientConn.
// The balancer should set the same custom user agent if it creates a
// ClientConn.
CustomUserAgent string
// Target contains the parsed address info of the dial target. It is the
// same resolver.Target as passed to the resolver. See the documentation for
// the resolver.Target type for details about what it contains.
Target resolver.Target
}
// Builder creates a balancer.
type Builder interface {
// Build creates a new balancer with the ClientConn.
Build(cc ClientConn, opts BuildOptions) Balancer
// Name returns the name of balancers built by this builder.
// It will be used to pick balancers (for example in service config).
Name() string
}
// ConfigParser parses load balancer configs.
type ConfigParser interface {
// ParseConfig parses the JSON load balancer config provided into an
// internal form or returns an error if the config is invalid. For future
// compatibility reasons, unknown fields in the config should be ignored.
ParseConfig(LoadBalancingConfigJSON json.RawMessage) (serviceconfig.LoadBalancingConfig, error)
}
// PickInfo contains additional information for the Pick operation.
type PickInfo struct {
// FullMethodName is the method name that NewClientStream() is called
// with. The canonical format is /service/Method.
FullMethodName string
// Ctx is the RPC's context, and may contain relevant RPC-level information
// like the outgoing header metadata.
Ctx context.Context
}
// DoneInfo contains additional information for done.
type DoneInfo struct {
// Err is the rpc error the RPC finished with. It could be nil.
Err error
// Trailer contains the metadata from the RPC's trailer, if present.
Trailer metadata.MD
// BytesSent indicates if any bytes have been sent to the server.
BytesSent bool
// BytesReceived indicates if any byte has been received from the server.
BytesReceived bool
// ServerLoad is the load received from server. It's usually sent as part of
// trailing metadata.
//
// The only supported type now is *orca_v3.LoadReport.
ServerLoad any
}
var (
// ErrNoSubConnAvailable indicates no SubConn is available for pick().
// gRPC will block the RPC until a new picker is available via UpdateState().
ErrNoSubConnAvailable = errors.New("no SubConn is available")
// ErrTransientFailure indicates all SubConns are in TransientFailure.
// WaitForReady RPCs will block, non-WaitForReady RPCs will fail.
//
// Deprecated: return an appropriate error based on the last resolution or
// connection attempt instead. The behavior is the same for any non-gRPC
// status error.
ErrTransientFailure = errors.New("all SubConns are in TransientFailure")
)
// PickResult contains information related to a connection chosen for an RPC.
type PickResult struct {
// SubConn is the connection to use for this pick, if its state is Ready.
// If the state is not Ready, gRPC will block the RPC until a new Picker is
// provided by the balancer (using ClientConn.UpdateState). The SubConn
// must be one returned by ClientConn.NewSubConn.
SubConn SubConn
// Done is called when the RPC is completed. If the SubConn is not ready,
// this will be called with a nil parameter. If the SubConn is not a valid
// type, Done may not be called. May be nil if the balancer does not wish
// to be notified when the RPC completes.
Done func(DoneInfo)
// Metadata provides a way for LB policies to inject arbitrary per-call
// metadata. Any metadata returned here will be merged with existing
// metadata added by the client application.
//
// LB policies with child policies are responsible for propagating metadata
// injected by their children to the ClientConn, as part of Pick().
Metadata metadata.MD
}
// TransientFailureError returns e. It exists for backward compatibility and
// will be deleted soon.
//
// Deprecated: no longer necessary, picker errors are treated this way by
// default.
func TransientFailureError(e error) error { return e }
// Picker is used by gRPC to pick a SubConn to send an RPC.
// Balancer is expected to generate a new picker from its snapshot every time its
// internal state has changed.
//
// The pickers used by gRPC can be updated by ClientConn.UpdateState().
type Picker interface {
// Pick returns the connection to use for this RPC and related information.
//
// Pick should not block. If the balancer needs to do I/O or any blocking
// or time-consuming work to service this call, it should return
// ErrNoSubConnAvailable, and the Pick call will be repeated by gRPC when
// the Picker is updated (using ClientConn.UpdateState).
//
// If an error is returned:
//
// - If the error is ErrNoSubConnAvailable, gRPC will block until a new
// Picker is provided by the balancer (using ClientConn.UpdateState).
//
// - If the error is a status error (implemented by the grpc/status
// package), gRPC will terminate the RPC with the code and message
// provided.
//
// - For all other errors, wait for ready RPCs will wait, but non-wait for
// ready RPCs will be terminated with this error's Error() string and
// status code Unavailable.
Pick(info PickInfo) (PickResult, error)
}
// Balancer takes input from gRPC, manages SubConns, and collects and aggregates
// the connectivity states.
//
// It also generates and updates the Picker used by gRPC to pick SubConns for RPCs.
//
// UpdateClientConnState, ResolverError, UpdateSubConnState, and Close are
// guaranteed to be called synchronously from the same goroutine. There's no
// guarantee on picker.Pick, it may be called anytime.
type Balancer interface {
// UpdateClientConnState is called by gRPC when the state of the ClientConn
// changes. If the error returned is ErrBadResolverState, the ClientConn
// will begin calling ResolveNow on the active name resolver with
// exponential backoff until a subsequent call to UpdateClientConnState
// returns a nil error. Any other errors are currently ignored.
UpdateClientConnState(ClientConnState) error
// ResolverError is called by gRPC when the name resolver reports an error.
ResolverError(error)
// UpdateSubConnState is called by gRPC when the state of a SubConn
// changes.
//
// Deprecated: Use NewSubConnOptions.StateListener when creating the
// SubConn instead.
UpdateSubConnState(SubConn, SubConnState)
// Close closes the balancer. The balancer is not currently required to
// call SubConn.Shutdown for its existing SubConns; however, this will be
// required in a future release, so it is recommended.
Close()
// ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter
// the IDLE state will not reconnect until SubConn.Connect is called.
ExitIdle()
}
// ExitIdler is an optional interface for balancers to implement. If
// implemented, ExitIdle will be called when ClientConn.Connect is called, if
// the ClientConn is idle. If unimplemented, ClientConn.Connect will cause
// all SubConns to connect.
//
// Deprecated: All balancers must implement this interface. This interface will
// be removed in a future release.
type ExitIdler interface {
// ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter
// the IDLE state will not reconnect until SubConn.Connect is called.
ExitIdle()
}
// ClientConnState describes the state of a ClientConn relevant to the
// balancer.
type ClientConnState struct {
ResolverState resolver.State
// The parsed load balancing configuration returned by the builder's
// ParseConfig method, if implemented.
BalancerConfig serviceconfig.LoadBalancingConfig
}
// ErrBadResolverState may be returned by UpdateClientConnState to indicate a
// problem with the provided name resolver data.
var ErrBadResolverState = errors.New("bad resolver state")

262
vendor/google.golang.org/grpc/balancer/base/balancer.go generated vendored Normal file
View File

@@ -0,0 +1,262 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package base
import (
"errors"
"fmt"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/resolver"
)
var logger = grpclog.Component("balancer")
type baseBuilder struct {
name string
pickerBuilder PickerBuilder
config Config
}
func (bb *baseBuilder) Build(cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer {
bal := &baseBalancer{
cc: cc,
pickerBuilder: bb.pickerBuilder,
subConns: resolver.NewAddressMapV2[balancer.SubConn](),
scStates: make(map[balancer.SubConn]connectivity.State),
csEvltr: &balancer.ConnectivityStateEvaluator{},
config: bb.config,
state: connectivity.Connecting,
}
// Initialize picker to a picker that always returns
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
// may call UpdateState with this picker.
bal.picker = NewErrPicker(balancer.ErrNoSubConnAvailable)
return bal
}
func (bb *baseBuilder) Name() string {
return bb.name
}
type baseBalancer struct {
cc balancer.ClientConn
pickerBuilder PickerBuilder
csEvltr *balancer.ConnectivityStateEvaluator
state connectivity.State
subConns *resolver.AddressMapV2[balancer.SubConn]
scStates map[balancer.SubConn]connectivity.State
picker balancer.Picker
config Config
resolverErr error // the last error reported by the resolver; cleared on successful resolution
connErr error // the last connection error; cleared upon leaving TransientFailure
}
func (b *baseBalancer) ResolverError(err error) {
b.resolverErr = err
if b.subConns.Len() == 0 {
b.state = connectivity.TransientFailure
}
if b.state != connectivity.TransientFailure {
// The picker will not change since the balancer does not currently
// report an error.
return
}
b.regeneratePicker()
b.cc.UpdateState(balancer.State{
ConnectivityState: b.state,
Picker: b.picker,
})
}
func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
// TODO: handle s.ResolverState.ServiceConfig?
if logger.V(2) {
logger.Info("base.baseBalancer: got new ClientConn state: ", s)
}
// Successful resolution; clear resolver error and ensure we return nil.
b.resolverErr = nil
// addrsSet is the set converted from addrs, it's used for quick lookup of an address.
addrsSet := resolver.NewAddressMapV2[any]()
for _, a := range s.ResolverState.Addresses {
addrsSet.Set(a, nil)
if _, ok := b.subConns.Get(a); !ok {
// a is a new address (not existing in b.subConns).
var sc balancer.SubConn
opts := balancer.NewSubConnOptions{
HealthCheckEnabled: b.config.HealthCheck,
StateListener: func(scs balancer.SubConnState) { b.updateSubConnState(sc, scs) },
}
sc, err := b.cc.NewSubConn([]resolver.Address{a}, opts)
if err != nil {
logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
continue
}
b.subConns.Set(a, sc)
b.scStates[sc] = connectivity.Idle
b.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle)
sc.Connect()
}
}
for _, a := range b.subConns.Keys() {
sc, _ := b.subConns.Get(a)
// a was removed by resolver.
if _, ok := addrsSet.Get(a); !ok {
sc.Shutdown()
b.subConns.Delete(a)
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
// The entry will be deleted in updateSubConnState.
}
}
// If resolver state contains no addresses, return an error so ClientConn
// will trigger re-resolve. Also records this as a resolver error, so when
// the overall state turns transient failure, the error message will have
// the zero address information.
if len(s.ResolverState.Addresses) == 0 {
b.ResolverError(errors.New("produced zero addresses"))
return balancer.ErrBadResolverState
}
b.regeneratePicker()
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})
return nil
}
// mergeErrors builds an error from the last connection error and the last
// resolver error. Must only be called if b.state is TransientFailure.
func (b *baseBalancer) mergeErrors() error {
// connErr must always be non-nil unless there are no SubConns, in which
// case resolverErr must be non-nil.
if b.connErr == nil {
return fmt.Errorf("last resolver error: %v", b.resolverErr)
}
if b.resolverErr == nil {
return fmt.Errorf("last connection error: %v", b.connErr)
}
return fmt.Errorf("last connection error: %v; last resolver error: %v", b.connErr, b.resolverErr)
}
// regeneratePicker takes a snapshot of the balancer, and generates a picker
// from it. The picker is
// - errPicker if the balancer is in TransientFailure,
// - built by the pickerBuilder with all READY SubConns otherwise.
func (b *baseBalancer) regeneratePicker() {
if b.state == connectivity.TransientFailure {
b.picker = NewErrPicker(b.mergeErrors())
return
}
readySCs := make(map[balancer.SubConn]SubConnInfo)
// Filter out all ready SCs from full subConn map.
for _, addr := range b.subConns.Keys() {
sc, _ := b.subConns.Get(addr)
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr}
}
}
b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})
}
// UpdateSubConnState is a nop because a StateListener is always set in NewSubConn.
func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
logger.Errorf("base.baseBalancer: UpdateSubConnState(%v, %+v) called unexpectedly", sc, state)
}
func (b *baseBalancer) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
s := state.ConnectivityState
if logger.V(2) {
logger.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
}
oldS, ok := b.scStates[sc]
if !ok {
if logger.V(2) {
logger.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
}
return
}
if oldS == connectivity.TransientFailure &&
(s == connectivity.Connecting || s == connectivity.Idle) {
// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent IDLE or
// CONNECTING transitions to prevent the aggregated state from being
// always CONNECTING when many backends exist but are all down.
if s == connectivity.Idle {
sc.Connect()
}
return
}
b.scStates[sc] = s
switch s {
case connectivity.Idle:
sc.Connect()
case connectivity.Shutdown:
// When an address was removed by resolver, b called Shutdown but kept
// the sc's state in scStates. Remove state for this sc here.
delete(b.scStates, sc)
case connectivity.TransientFailure:
// Save error to be reported via picker.
b.connErr = state.ConnectionError
}
b.state = b.csEvltr.RecordTransition(oldS, s)
// Regenerate picker when one of the following happens:
// - this sc entered or left ready
// - the aggregated state of balancer is TransientFailure
// (may need to update error message)
if (s == connectivity.Ready) != (oldS == connectivity.Ready) ||
b.state == connectivity.TransientFailure {
b.regeneratePicker()
}
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})
}
// Close is a nop because base balancer doesn't have internal state to clean up,
// and it doesn't need to call Shutdown for the SubConns.
func (b *baseBalancer) Close() {
}
// ExitIdle is a nop because the base balancer attempts to stay connected to
// all SubConns at all times.
func (b *baseBalancer) ExitIdle() {
}
// NewErrPicker returns a Picker that always returns err on Pick().
func NewErrPicker(err error) balancer.Picker {
return &errPicker{err: err}
}
// NewErrPickerV2 is temporarily defined for backward compatibility reasons.
//
// Deprecated: use NewErrPicker instead.
var NewErrPickerV2 = NewErrPicker
type errPicker struct {
err error // Pick() always returns this err.
}
func (p *errPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
return balancer.PickResult{}, p.err
}

71
vendor/google.golang.org/grpc/balancer/base/base.go generated vendored Normal file
View File

@@ -0,0 +1,71 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package base defines a balancer base that can be used to build balancers with
// different picking algorithms.
//
// The base balancer creates a new SubConn for each resolved address. The
// provided picker will only be notified about READY SubConns.
//
// This package is the base of round_robin balancer, its purpose is to be used
// to build round_robin like balancers with complex picking algorithms.
// Balancers with more complicated logic should try to implement a balancer
// builder from scratch.
//
// All APIs in this package are experimental.
package base
import (
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/resolver"
)
// PickerBuilder creates balancer.Picker.
type PickerBuilder interface {
// Build returns a picker that will be used by gRPC to pick a SubConn.
Build(info PickerBuildInfo) balancer.Picker
}
// PickerBuildInfo contains information needed by the picker builder to
// construct a picker.
type PickerBuildInfo struct {
// ReadySCs is a map from all ready SubConns to the Addresses used to
// create them.
ReadySCs map[balancer.SubConn]SubConnInfo
}
// SubConnInfo contains information about a SubConn created by the base
// balancer.
type SubConnInfo struct {
Address resolver.Address // the address used to create this SubConn
}
// Config contains the config info about the base balancer builder.
type Config struct {
// HealthCheck indicates whether health checking should be enabled for this specific balancer.
HealthCheck bool
}
// NewBalancerBuilder returns a base balancer builder configured by the provided config.
func NewBalancerBuilder(name string, pb PickerBuilder, config Config) balancer.Builder {
return &baseBuilder{
name: name,
pickerBuilder: pb,
config: config,
}
}

View File

@@ -0,0 +1,74 @@
/*
*
* Copyright 2022 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package balancer
import "google.golang.org/grpc/connectivity"
// ConnectivityStateEvaluator takes the connectivity states of multiple SubConns
// and returns one aggregated connectivity state.
//
// It's not thread safe.
type ConnectivityStateEvaluator struct {
numReady uint64 // Number of addrConns in ready state.
numConnecting uint64 // Number of addrConns in connecting state.
numTransientFailure uint64 // Number of addrConns in transient failure state.
numIdle uint64 // Number of addrConns in idle state.
}
// RecordTransition records state change happening in subConn and based on that
// it evaluates what aggregated state should be.
//
// - If at least one SubConn in Ready, the aggregated state is Ready;
// - Else if at least one SubConn in Connecting, the aggregated state is Connecting;
// - Else if at least one SubConn is Idle, the aggregated state is Idle;
// - Else if at least one SubConn is TransientFailure (or there are no SubConns), the aggregated state is Transient Failure.
//
// Shutdown is not considered.
func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState connectivity.State) connectivity.State {
// Update counters.
for idx, state := range []connectivity.State{oldState, newState} {
updateVal := 2*uint64(idx) - 1 // -1 for oldState and +1 for new.
switch state {
case connectivity.Ready:
cse.numReady += updateVal
case connectivity.Connecting:
cse.numConnecting += updateVal
case connectivity.TransientFailure:
cse.numTransientFailure += updateVal
case connectivity.Idle:
cse.numIdle += updateVal
}
}
return cse.CurrentState()
}
// CurrentState returns the current aggregate conn state by evaluating the counters
func (cse *ConnectivityStateEvaluator) CurrentState() connectivity.State {
// Evaluate.
if cse.numReady > 0 {
return connectivity.Ready
}
if cse.numConnecting > 0 {
return connectivity.Connecting
}
if cse.numIdle > 0 {
return connectivity.Idle
}
return connectivity.TransientFailure
}

View File

@@ -0,0 +1,389 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package endpointsharding implements a load balancing policy that manages
// homogeneous child policies each owning a single endpoint.
//
// # Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
// later release.
package endpointsharding
import (
"errors"
rand "math/rand/v2"
"sync"
"sync/atomic"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/resolver"
)
var randIntN = rand.IntN
// ChildState is the balancer state of a child along with the endpoint which
// identifies the child balancer.
type ChildState struct {
Endpoint resolver.Endpoint
State balancer.State
// Balancer exposes only the ExitIdler interface of the child LB policy.
// Other methods of the child policy are called only by endpointsharding.
Balancer ExitIdler
}
// ExitIdler provides access to only the ExitIdle method of the child balancer.
type ExitIdler interface {
// ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter
// the IDLE state will not reconnect until SubConn.Connect is called.
ExitIdle()
}
// Options are the options to configure the behaviour of the
// endpointsharding balancer.
type Options struct {
// DisableAutoReconnect allows the balancer to keep child balancer in the
// IDLE state until they are explicitly triggered to exit using the
// ChildState obtained from the endpointsharding picker. When set to false,
// the endpointsharding balancer will automatically call ExitIdle on child
// connections that report IDLE.
DisableAutoReconnect bool
}
// ChildBuilderFunc creates a new balancer with the ClientConn. It has the same
// type as the balancer.Builder.Build method.
type ChildBuilderFunc func(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer
// NewBalancer returns a load balancing policy that manages homogeneous child
// policies each owning a single endpoint. The endpointsharding balancer
// forwards the LoadBalancingConfig in ClientConn state updates to its children.
func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions, childBuilder ChildBuilderFunc, esOpts Options) balancer.Balancer {
es := &endpointSharding{
cc: cc,
bOpts: opts,
esOpts: esOpts,
childBuilder: childBuilder,
}
es.children.Store(resolver.NewEndpointMap[*balancerWrapper]())
return es
}
// endpointSharding is a balancer that wraps child balancers. It creates a child
// balancer with child config for every unique Endpoint received. It updates the
// child states on any update from parent or child.
type endpointSharding struct {
cc balancer.ClientConn
bOpts balancer.BuildOptions
esOpts Options
childBuilder ChildBuilderFunc
// childMu synchronizes calls to any single child. It must be held for all
// calls into a child. To avoid deadlocks, do not acquire childMu while
// holding mu.
childMu sync.Mutex
children atomic.Pointer[resolver.EndpointMap[*balancerWrapper]]
// inhibitChildUpdates is set during UpdateClientConnState/ResolverError
// calls (calls to children will each produce an update, only want one
// update).
inhibitChildUpdates atomic.Bool
// mu synchronizes access to the state stored in balancerWrappers in the
// children field. mu must not be held during calls into a child since
// synchronous calls back from the child may require taking mu, causing a
// deadlock. To avoid deadlocks, do not acquire childMu while holding mu.
mu sync.Mutex
}
// rotateEndpoints returns a slice of all the input endpoints rotated a random
// amount.
func rotateEndpoints(es []resolver.Endpoint) []resolver.Endpoint {
les := len(es)
if les == 0 {
return es
}
r := randIntN(les)
// Make a copy to avoid mutating data beyond the end of es.
ret := make([]resolver.Endpoint, les)
copy(ret, es[r:])
copy(ret[les-r:], es[:r])
return ret
}
// UpdateClientConnState creates a child for new endpoints and deletes children
// for endpoints that are no longer present. It also updates all the children,
// and sends a single synchronous update of the childrens' aggregated state at
// the end of the UpdateClientConnState operation. If any endpoint has no
// addresses it will ignore that endpoint. Otherwise, returns first error found
// from a child, but fully processes the new update.
func (es *endpointSharding) UpdateClientConnState(state balancer.ClientConnState) error {
es.childMu.Lock()
defer es.childMu.Unlock()
es.inhibitChildUpdates.Store(true)
defer func() {
es.inhibitChildUpdates.Store(false)
es.updateState()
}()
var ret error
children := es.children.Load()
newChildren := resolver.NewEndpointMap[*balancerWrapper]()
// Update/Create new children.
for _, endpoint := range rotateEndpoints(state.ResolverState.Endpoints) {
if _, ok := newChildren.Get(endpoint); ok {
// Endpoint child was already created, continue to avoid duplicate
// update.
continue
}
childBalancer, ok := children.Get(endpoint)
if ok {
// Endpoint attributes may have changed, update the stored endpoint.
es.mu.Lock()
childBalancer.childState.Endpoint = endpoint
es.mu.Unlock()
} else {
childBalancer = &balancerWrapper{
childState: ChildState{Endpoint: endpoint},
ClientConn: es.cc,
es: es,
}
childBalancer.childState.Balancer = childBalancer
childBalancer.child = es.childBuilder(childBalancer, es.bOpts)
}
newChildren.Set(endpoint, childBalancer)
if err := childBalancer.updateClientConnStateLocked(balancer.ClientConnState{
BalancerConfig: state.BalancerConfig,
ResolverState: resolver.State{
Endpoints: []resolver.Endpoint{endpoint},
Attributes: state.ResolverState.Attributes,
},
}); err != nil && ret == nil {
// Return first error found, and always commit full processing of
// updating children. If desired to process more specific errors
// across all endpoints, caller should make these specific
// validations, this is a current limitation for simplicity sake.
ret = err
}
}
// Delete old children that are no longer present.
for _, e := range children.Keys() {
child, _ := children.Get(e)
if _, ok := newChildren.Get(e); !ok {
child.closeLocked()
}
}
es.children.Store(newChildren)
if newChildren.Len() == 0 {
return balancer.ErrBadResolverState
}
return ret
}
// ResolverError forwards the resolver error to all of the endpointSharding's
// children and sends a single synchronous update of the childStates at the end
// of the ResolverError operation.
func (es *endpointSharding) ResolverError(err error) {
es.childMu.Lock()
defer es.childMu.Unlock()
es.inhibitChildUpdates.Store(true)
defer func() {
es.inhibitChildUpdates.Store(false)
es.updateState()
}()
children := es.children.Load()
for _, child := range children.Values() {
child.resolverErrorLocked(err)
}
}
func (es *endpointSharding) UpdateSubConnState(balancer.SubConn, balancer.SubConnState) {
// UpdateSubConnState is deprecated.
}
func (es *endpointSharding) Close() {
es.childMu.Lock()
defer es.childMu.Unlock()
children := es.children.Load()
for _, child := range children.Values() {
child.closeLocked()
}
}
func (es *endpointSharding) ExitIdle() {
es.childMu.Lock()
defer es.childMu.Unlock()
for _, bw := range es.children.Load().Values() {
if !bw.isClosed {
bw.child.ExitIdle()
}
}
}
// updateState updates this component's state. It sends the aggregated state,
// and a picker with round robin behavior with all the child states present if
// needed.
func (es *endpointSharding) updateState() {
if es.inhibitChildUpdates.Load() {
return
}
var readyPickers, connectingPickers, idlePickers, transientFailurePickers []balancer.Picker
es.mu.Lock()
defer es.mu.Unlock()
children := es.children.Load()
childStates := make([]ChildState, 0, children.Len())
for _, child := range children.Values() {
childState := child.childState
childStates = append(childStates, childState)
childPicker := childState.State.Picker
switch childState.State.ConnectivityState {
case connectivity.Ready:
readyPickers = append(readyPickers, childPicker)
case connectivity.Connecting:
connectingPickers = append(connectingPickers, childPicker)
case connectivity.Idle:
idlePickers = append(idlePickers, childPicker)
case connectivity.TransientFailure:
transientFailurePickers = append(transientFailurePickers, childPicker)
// connectivity.Shutdown shouldn't appear.
}
}
// Construct the round robin picker based off the aggregated state. Whatever
// the aggregated state, use the pickers present that are currently in that
// state only.
var aggState connectivity.State
var pickers []balancer.Picker
if len(readyPickers) >= 1 {
aggState = connectivity.Ready
pickers = readyPickers
} else if len(connectingPickers) >= 1 {
aggState = connectivity.Connecting
pickers = connectingPickers
} else if len(idlePickers) >= 1 {
aggState = connectivity.Idle
pickers = idlePickers
} else if len(transientFailurePickers) >= 1 {
aggState = connectivity.TransientFailure
pickers = transientFailurePickers
} else {
aggState = connectivity.TransientFailure
pickers = []balancer.Picker{base.NewErrPicker(errors.New("no children to pick from"))}
} // No children (resolver error before valid update).
p := &pickerWithChildStates{
pickers: pickers,
childStates: childStates,
next: uint32(randIntN(len(pickers))),
}
es.cc.UpdateState(balancer.State{
ConnectivityState: aggState,
Picker: p,
})
}
// pickerWithChildStates delegates to the pickers it holds in a round robin
// fashion. It also contains the childStates of all the endpointSharding's
// children.
type pickerWithChildStates struct {
pickers []balancer.Picker
childStates []ChildState
next uint32
}
func (p *pickerWithChildStates) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
nextIndex := atomic.AddUint32(&p.next, 1)
picker := p.pickers[nextIndex%uint32(len(p.pickers))]
return picker.Pick(info)
}
// ChildStatesFromPicker returns the state of all the children managed by the
// endpoint sharding balancer that created this picker.
func ChildStatesFromPicker(picker balancer.Picker) []ChildState {
p, ok := picker.(*pickerWithChildStates)
if !ok {
return nil
}
return p.childStates
}
// balancerWrapper is a wrapper of a balancer. It ID's a child balancer by
// endpoint, and persists recent child balancer state.
type balancerWrapper struct {
// The following fields are initialized at build time and read-only after
// that and therefore do not need to be guarded by a mutex.
// child contains the wrapped balancer. Access its methods only through
// methods on balancerWrapper to ensure proper synchronization
child balancer.Balancer
balancer.ClientConn // embed to intercept UpdateState, doesn't deal with SubConns
es *endpointSharding
// Access to the following fields is guarded by es.mu.
childState ChildState
isClosed bool
}
func (bw *balancerWrapper) UpdateState(state balancer.State) {
bw.es.mu.Lock()
bw.childState.State = state
bw.es.mu.Unlock()
if state.ConnectivityState == connectivity.Idle && !bw.es.esOpts.DisableAutoReconnect {
bw.ExitIdle()
}
bw.es.updateState()
}
// ExitIdle pings an IDLE child balancer to exit idle in a new goroutine to
// avoid deadlocks due to synchronous balancer state updates.
func (bw *balancerWrapper) ExitIdle() {
go func() {
bw.es.childMu.Lock()
if !bw.isClosed {
bw.child.ExitIdle()
}
bw.es.childMu.Unlock()
}()
}
// updateClientConnStateLocked delivers the ClientConnState to the child
// balancer. Callers must hold the child mutex of the parent endpointsharding
// balancer.
func (bw *balancerWrapper) updateClientConnStateLocked(ccs balancer.ClientConnState) error {
return bw.child.UpdateClientConnState(ccs)
}
// closeLocked closes the child balancer. Callers must hold the child mutext of
// the parent endpointsharding balancer.
func (bw *balancerWrapper) closeLocked() {
bw.child.Close()
bw.isClosed = true
}
func (bw *balancerWrapper) resolverErrorLocked(err error) {
bw.child.ResolverError(err)
}

View File

@@ -0,0 +1,51 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package state declares grpclb types to be set by resolvers wishing to pass
// information to grpclb via resolver.State Attributes.
package state
import (
"google.golang.org/grpc/resolver"
)
// keyType is the key to use for storing State in Attributes.
type keyType string
const key = keyType("grpc.grpclb.state")
// State contains gRPCLB-relevant data passed from the name resolver.
type State struct {
// BalancerAddresses contains the remote load balancer address(es). If
// set, overrides any resolver-provided addresses with Type of GRPCLB.
BalancerAddresses []resolver.Address
}
// Set returns a copy of the provided state with attributes containing s. s's
// data should not be mutated after calling Set.
func Set(state resolver.State, s *State) resolver.State {
state.Attributes = state.Attributes.WithValue(key, s)
return state
}
// Get returns the grpclb State in the resolver.State, or nil if not present.
// The returned data should not be mutated.
func Get(state resolver.State) *State {
s, _ := state.Attributes.Value(key).(*State)
return s
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package internal contains code internal to the pickfirst package.
package internal
import (
rand "math/rand/v2"
"time"
)
var (
// RandShuffle pseudo-randomizes the order of addresses.
RandShuffle = rand.Shuffle
// TimeAfterFunc allows mocking the timer for testing connection delay
// related functionality.
TimeAfterFunc = func(d time.Duration, f func()) func() {
timer := time.AfterFunc(d, f)
return func() { timer.Stop() }
}
)

View File

@@ -0,0 +1,908 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package pickfirst contains the pick_first load balancing policy which
// is the universal leaf policy.
package pickfirst
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/netip"
"sync"
"time"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/pickfirst/internal"
"google.golang.org/grpc/connectivity"
expstats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/grpclog"
internalgrpclog "google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/pretty"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
)
func init() {
balancer.Register(pickfirstBuilder{})
}
// Name is the name of the pick_first balancer.
const Name = "pick_first"
// enableHealthListenerKeyType is a unique key type used in resolver
// attributes to indicate whether the health listener usage is enabled.
type enableHealthListenerKeyType struct{}
var (
logger = grpclog.Component("pick-first-leaf-lb")
disconnectionsMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{
Name: "grpc.lb.pick_first.disconnections",
Description: "EXPERIMENTAL. Number of times the selected subchannel becomes disconnected.",
Unit: "{disconnection}",
Labels: []string{"grpc.target"},
Default: false,
})
connectionAttemptsSucceededMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{
Name: "grpc.lb.pick_first.connection_attempts_succeeded",
Description: "EXPERIMENTAL. Number of successful connection attempts.",
Unit: "{attempt}",
Labels: []string{"grpc.target"},
Default: false,
})
connectionAttemptsFailedMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{
Name: "grpc.lb.pick_first.connection_attempts_failed",
Description: "EXPERIMENTAL. Number of failed connection attempts.",
Unit: "{attempt}",
Labels: []string{"grpc.target"},
Default: false,
})
)
const (
// TODO: change to pick-first when this becomes the default pick_first policy.
logPrefix = "[pick-first-leaf-lb %p] "
// connectionDelayInterval is the time to wait for during the happy eyeballs
// pass before starting the next connection attempt.
connectionDelayInterval = 250 * time.Millisecond
)
type ipAddrFamily int
const (
// ipAddrFamilyUnknown represents strings that can't be parsed as an IP
// address.
ipAddrFamilyUnknown ipAddrFamily = iota
ipAddrFamilyV4
ipAddrFamilyV6
)
type pickfirstBuilder struct{}
func (pickfirstBuilder) Build(cc balancer.ClientConn, bo balancer.BuildOptions) balancer.Balancer {
b := &pickfirstBalancer{
cc: cc,
target: bo.Target.String(),
metricsRecorder: cc.MetricsRecorder(),
subConns: resolver.NewAddressMapV2[*scData](),
state: connectivity.Connecting,
cancelConnectionTimer: func() {},
}
b.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(logPrefix, b))
return b
}
func (b pickfirstBuilder) Name() string {
return Name
}
func (pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
var cfg pfConfig
if err := json.Unmarshal(js, &cfg); err != nil {
return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err)
}
return cfg, nil
}
// EnableHealthListener updates the state to configure pickfirst for using a
// generic health listener.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
func EnableHealthListener(state resolver.State) resolver.State {
state.Attributes = state.Attributes.WithValue(enableHealthListenerKeyType{}, true)
return state
}
type pfConfig struct {
serviceconfig.LoadBalancingConfig `json:"-"`
// If set to true, instructs the LB policy to shuffle the order of the list
// of endpoints received from the name resolver before attempting to
// connect to them.
ShuffleAddressList bool `json:"shuffleAddressList"`
}
// scData keeps track of the current state of the subConn.
// It is not safe for concurrent access.
type scData struct {
// The following fields are initialized at build time and read-only after
// that.
subConn balancer.SubConn
addr resolver.Address
rawConnectivityState connectivity.State
// The effective connectivity state based on raw connectivity, health state
// and after following sticky TransientFailure behaviour defined in A62.
effectiveState connectivity.State
lastErr error
connectionFailedInFirstPass bool
}
func (b *pickfirstBalancer) newSCData(addr resolver.Address) (*scData, error) {
sd := &scData{
rawConnectivityState: connectivity.Idle,
effectiveState: connectivity.Idle,
addr: addr,
}
sc, err := b.cc.NewSubConn([]resolver.Address{addr}, balancer.NewSubConnOptions{
StateListener: func(state balancer.SubConnState) {
b.updateSubConnState(sd, state)
},
})
if err != nil {
return nil, err
}
sd.subConn = sc
return sd, nil
}
type pickfirstBalancer struct {
// The following fields are initialized at build time and read-only after
// that and therefore do not need to be guarded by a mutex.
logger *internalgrpclog.PrefixLogger
cc balancer.ClientConn
target string
metricsRecorder expstats.MetricsRecorder // guaranteed to be non nil
// The mutex is used to ensure synchronization of updates triggered
// from the idle picker and the already serialized resolver,
// SubConn state updates.
mu sync.Mutex
// State reported to the channel based on SubConn states and resolver
// updates.
state connectivity.State
// scData for active subonns mapped by address.
subConns *resolver.AddressMapV2[*scData]
addressList addressList
firstPass bool
numTF int
cancelConnectionTimer func()
healthCheckingEnabled bool
}
// ResolverError is called by the ClientConn when the name resolver produces
// an error or when pickfirst determined the resolver update to be invalid.
func (b *pickfirstBalancer) ResolverError(err error) {
b.mu.Lock()
defer b.mu.Unlock()
b.resolverErrorLocked(err)
}
func (b *pickfirstBalancer) resolverErrorLocked(err error) {
if b.logger.V(2) {
b.logger.Infof("Received error from the name resolver: %v", err)
}
// The picker will not change since the balancer does not currently
// report an error. If the balancer hasn't received a single good resolver
// update yet, transition to TRANSIENT_FAILURE.
if b.state != connectivity.TransientFailure && b.addressList.size() > 0 {
if b.logger.V(2) {
b.logger.Infof("Ignoring resolver error because balancer is using a previous good update.")
}
return
}
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.TransientFailure,
Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)},
})
}
func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
b.mu.Lock()
defer b.mu.Unlock()
b.cancelConnectionTimer()
if len(state.ResolverState.Addresses) == 0 && len(state.ResolverState.Endpoints) == 0 {
// Cleanup state pertaining to the previous resolver state.
// Treat an empty address list like an error by calling b.ResolverError.
b.closeSubConnsLocked()
b.addressList.updateAddrs(nil)
b.resolverErrorLocked(errors.New("produced zero addresses"))
return balancer.ErrBadResolverState
}
b.healthCheckingEnabled = state.ResolverState.Attributes.Value(enableHealthListenerKeyType{}) != nil
cfg, ok := state.BalancerConfig.(pfConfig)
if state.BalancerConfig != nil && !ok {
return fmt.Errorf("pickfirst: received illegal BalancerConfig (type %T): %v: %w", state.BalancerConfig, state.BalancerConfig, balancer.ErrBadResolverState)
}
if b.logger.V(2) {
b.logger.Infof("Received new config %s, resolver state %s", pretty.ToJSON(cfg), pretty.ToJSON(state.ResolverState))
}
var newAddrs []resolver.Address
if endpoints := state.ResolverState.Endpoints; len(endpoints) != 0 {
// Perform the optional shuffling described in gRFC A62. The shuffling
// will change the order of endpoints but not touch the order of the
// addresses within each endpoint. - A61
if cfg.ShuffleAddressList {
endpoints = append([]resolver.Endpoint{}, endpoints...)
internal.RandShuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] })
}
// "Flatten the list by concatenating the ordered list of addresses for
// each of the endpoints, in order." - A61
for _, endpoint := range endpoints {
newAddrs = append(newAddrs, endpoint.Addresses...)
}
} else {
// Endpoints not set, process addresses until we migrate resolver
// emissions fully to Endpoints. The top channel does wrap emitted
// addresses with endpoints, however some balancers such as weighted
// target do not forward the corresponding correct endpoints down/split
// endpoints properly. Once all balancers correctly forward endpoints
// down, can delete this else conditional.
newAddrs = state.ResolverState.Addresses
if cfg.ShuffleAddressList {
newAddrs = append([]resolver.Address{}, newAddrs...)
internal.RandShuffle(len(newAddrs), func(i, j int) { newAddrs[i], newAddrs[j] = newAddrs[j], newAddrs[i] })
}
}
// If an address appears in multiple endpoints or in the same endpoint
// multiple times, we keep it only once. We will create only one SubConn
// for the address because an AddressMap is used to store SubConns.
// Not de-duplicating would result in attempting to connect to the same
// SubConn multiple times in the same pass. We don't want this.
newAddrs = deDupAddresses(newAddrs)
newAddrs = interleaveAddresses(newAddrs)
prevAddr := b.addressList.currentAddress()
prevSCData, found := b.subConns.Get(prevAddr)
prevAddrsCount := b.addressList.size()
isPrevRawConnectivityStateReady := found && prevSCData.rawConnectivityState == connectivity.Ready
b.addressList.updateAddrs(newAddrs)
// If the previous ready SubConn exists in new address list,
// keep this connection and don't create new SubConns.
if isPrevRawConnectivityStateReady && b.addressList.seekTo(prevAddr) {
return nil
}
b.reconcileSubConnsLocked(newAddrs)
// If it's the first resolver update or the balancer was already READY
// (but the new address list does not contain the ready SubConn) or
// CONNECTING, enter CONNECTING.
// We may be in TRANSIENT_FAILURE due to a previous empty address list,
// we should still enter CONNECTING because the sticky TF behaviour
// mentioned in A62 applies only when the TRANSIENT_FAILURE is reported
// due to connectivity failures.
if isPrevRawConnectivityStateReady || b.state == connectivity.Connecting || prevAddrsCount == 0 {
// Start connection attempt at first address.
b.forceUpdateConcludedStateLocked(balancer.State{
ConnectivityState: connectivity.Connecting,
Picker: &picker{err: balancer.ErrNoSubConnAvailable},
})
b.startFirstPassLocked()
} else if b.state == connectivity.TransientFailure {
// If we're in TRANSIENT_FAILURE, we stay in TRANSIENT_FAILURE until
// we're READY. See A62.
b.startFirstPassLocked()
}
return nil
}
// UpdateSubConnState is unused as a StateListener is always registered when
// creating SubConns.
func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) {
b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", subConn, state)
}
func (b *pickfirstBalancer) Close() {
b.mu.Lock()
defer b.mu.Unlock()
b.closeSubConnsLocked()
b.cancelConnectionTimer()
b.state = connectivity.Shutdown
}
// ExitIdle moves the balancer out of idle state. It can be called concurrently
// by the idlePicker and clientConn so access to variables should be
// synchronized.
func (b *pickfirstBalancer) ExitIdle() {
b.mu.Lock()
defer b.mu.Unlock()
if b.state == connectivity.Idle {
// Move the balancer into CONNECTING state immediately. This is done to
// avoid staying in IDLE if a resolver update arrives before the first
// SubConn reports CONNECTING.
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Connecting,
Picker: &picker{err: balancer.ErrNoSubConnAvailable},
})
b.startFirstPassLocked()
}
}
func (b *pickfirstBalancer) startFirstPassLocked() {
b.firstPass = true
b.numTF = 0
// Reset the connection attempt record for existing SubConns.
for _, sd := range b.subConns.Values() {
sd.connectionFailedInFirstPass = false
}
b.requestConnectionLocked()
}
func (b *pickfirstBalancer) closeSubConnsLocked() {
for _, sd := range b.subConns.Values() {
sd.subConn.Shutdown()
}
b.subConns = resolver.NewAddressMapV2[*scData]()
}
// deDupAddresses ensures that each address appears only once in the slice.
func deDupAddresses(addrs []resolver.Address) []resolver.Address {
seenAddrs := resolver.NewAddressMapV2[bool]()
retAddrs := []resolver.Address{}
for _, addr := range addrs {
if _, ok := seenAddrs.Get(addr); ok {
continue
}
seenAddrs.Set(addr, true)
retAddrs = append(retAddrs, addr)
}
return retAddrs
}
// interleaveAddresses interleaves addresses of both families (IPv4 and IPv6)
// as per RFC-8305 section 4.
// Whichever address family is first in the list is followed by an address of
// the other address family; that is, if the first address in the list is IPv6,
// then the first IPv4 address should be moved up in the list to be second in
// the list. It doesn't support configuring "First Address Family Count", i.e.
// there will always be a single member of the first address family at the
// beginning of the interleaved list.
// Addresses that are neither IPv4 nor IPv6 are treated as part of a third
// "unknown" family for interleaving.
// See: https://datatracker.ietf.org/doc/html/rfc8305#autoid-6
func interleaveAddresses(addrs []resolver.Address) []resolver.Address {
familyAddrsMap := map[ipAddrFamily][]resolver.Address{}
interleavingOrder := []ipAddrFamily{}
for _, addr := range addrs {
family := addressFamily(addr.Addr)
if _, found := familyAddrsMap[family]; !found {
interleavingOrder = append(interleavingOrder, family)
}
familyAddrsMap[family] = append(familyAddrsMap[family], addr)
}
interleavedAddrs := make([]resolver.Address, 0, len(addrs))
for curFamilyIdx := 0; len(interleavedAddrs) < len(addrs); curFamilyIdx = (curFamilyIdx + 1) % len(interleavingOrder) {
// Some IP types may have fewer addresses than others, so we look for
// the next type that has a remaining member to add to the interleaved
// list.
family := interleavingOrder[curFamilyIdx]
remainingMembers := familyAddrsMap[family]
if len(remainingMembers) > 0 {
interleavedAddrs = append(interleavedAddrs, remainingMembers[0])
familyAddrsMap[family] = remainingMembers[1:]
}
}
return interleavedAddrs
}
// addressFamily returns the ipAddrFamily after parsing the address string.
// If the address isn't of the format "ip-address:port", it returns
// ipAddrFamilyUnknown. The address may be valid even if it's not an IP when
// using a resolver like passthrough where the address may be a hostname in
// some format that the dialer can resolve.
func addressFamily(address string) ipAddrFamily {
// Parse the IP after removing the port.
host, _, err := net.SplitHostPort(address)
if err != nil {
return ipAddrFamilyUnknown
}
ip, err := netip.ParseAddr(host)
if err != nil {
return ipAddrFamilyUnknown
}
switch {
case ip.Is4() || ip.Is4In6():
return ipAddrFamilyV4
case ip.Is6():
return ipAddrFamilyV6
default:
return ipAddrFamilyUnknown
}
}
// reconcileSubConnsLocked updates the active subchannels based on a new address
// list from the resolver. It does this by:
// - closing subchannels: any existing subchannels associated with addresses
// that are no longer in the updated list are shut down.
// - removing subchannels: entries for these closed subchannels are removed
// from the subchannel map.
//
// This ensures that the subchannel map accurately reflects the current set of
// addresses received from the name resolver.
func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address) {
newAddrsMap := resolver.NewAddressMapV2[bool]()
for _, addr := range newAddrs {
newAddrsMap.Set(addr, true)
}
for _, oldAddr := range b.subConns.Keys() {
if _, ok := newAddrsMap.Get(oldAddr); ok {
continue
}
val, _ := b.subConns.Get(oldAddr)
val.subConn.Shutdown()
b.subConns.Delete(oldAddr)
}
}
// shutdownRemainingLocked shuts down remaining subConns. Called when a subConn
// becomes ready, which means that all other subConn must be shutdown.
func (b *pickfirstBalancer) shutdownRemainingLocked(selected *scData) {
b.cancelConnectionTimer()
for _, sd := range b.subConns.Values() {
if sd.subConn != selected.subConn {
sd.subConn.Shutdown()
}
}
b.subConns = resolver.NewAddressMapV2[*scData]()
b.subConns.Set(selected.addr, selected)
}
// requestConnectionLocked starts connecting on the subchannel corresponding to
// the current address. If no subchannel exists, one is created. If the current
// subchannel is in TransientFailure, a connection to the next address is
// attempted until a subchannel is found.
func (b *pickfirstBalancer) requestConnectionLocked() {
if !b.addressList.isValid() {
return
}
var lastErr error
for valid := true; valid; valid = b.addressList.increment() {
curAddr := b.addressList.currentAddress()
sd, ok := b.subConns.Get(curAddr)
if !ok {
var err error
// We want to assign the new scData to sd from the outer scope,
// hence we can't use := below.
sd, err = b.newSCData(curAddr)
if err != nil {
// This should never happen, unless the clientConn is being shut
// down.
if b.logger.V(2) {
b.logger.Infof("Failed to create a subConn for address %v: %v", curAddr.String(), err)
}
// Do nothing, the LB policy will be closed soon.
return
}
b.subConns.Set(curAddr, sd)
}
switch sd.rawConnectivityState {
case connectivity.Idle:
sd.subConn.Connect()
b.scheduleNextConnectionLocked()
return
case connectivity.TransientFailure:
// The SubConn is being re-used and failed during a previous pass
// over the addressList. It has not completed backoff yet.
// Mark it as having failed and try the next address.
sd.connectionFailedInFirstPass = true
lastErr = sd.lastErr
continue
case connectivity.Connecting:
// Wait for the connection attempt to complete or the timer to fire
// before attempting the next address.
b.scheduleNextConnectionLocked()
return
default:
b.logger.Errorf("SubConn with unexpected state %v present in SubConns map.", sd.rawConnectivityState)
return
}
}
// All the remaining addresses in the list are in TRANSIENT_FAILURE, end the
// first pass if possible.
b.endFirstPassIfPossibleLocked(lastErr)
}
func (b *pickfirstBalancer) scheduleNextConnectionLocked() {
b.cancelConnectionTimer()
if !b.addressList.hasNext() {
return
}
curAddr := b.addressList.currentAddress()
cancelled := false // Access to this is protected by the balancer's mutex.
closeFn := internal.TimeAfterFunc(connectionDelayInterval, func() {
b.mu.Lock()
defer b.mu.Unlock()
// If the scheduled task is cancelled while acquiring the mutex, return.
if cancelled {
return
}
if b.logger.V(2) {
b.logger.Infof("Happy Eyeballs timer expired while waiting for connection to %q.", curAddr.Addr)
}
if b.addressList.increment() {
b.requestConnectionLocked()
}
})
// Access to the cancellation callback held by the balancer is guarded by
// the balancer's mutex, so it's safe to set the boolean from the callback.
b.cancelConnectionTimer = sync.OnceFunc(func() {
cancelled = true
closeFn()
})
}
func (b *pickfirstBalancer) updateSubConnState(sd *scData, newState balancer.SubConnState) {
b.mu.Lock()
defer b.mu.Unlock()
oldState := sd.rawConnectivityState
sd.rawConnectivityState = newState.ConnectivityState
// Previously relevant SubConns can still callback with state updates.
// To prevent pickers from returning these obsolete SubConns, this logic
// is included to check if the current list of active SubConns includes this
// SubConn.
if !b.isActiveSCData(sd) {
return
}
if newState.ConnectivityState == connectivity.Shutdown {
sd.effectiveState = connectivity.Shutdown
return
}
// Record a connection attempt when exiting CONNECTING.
if newState.ConnectivityState == connectivity.TransientFailure {
sd.connectionFailedInFirstPass = true
connectionAttemptsFailedMetric.Record(b.metricsRecorder, 1, b.target)
}
if newState.ConnectivityState == connectivity.Ready {
connectionAttemptsSucceededMetric.Record(b.metricsRecorder, 1, b.target)
b.shutdownRemainingLocked(sd)
if !b.addressList.seekTo(sd.addr) {
// This should not fail as we should have only one SubConn after
// entering READY. The SubConn should be present in the addressList.
b.logger.Errorf("Address %q not found address list in %v", sd.addr, b.addressList.addresses)
return
}
if !b.healthCheckingEnabled {
if b.logger.V(2) {
b.logger.Infof("SubConn %p reported connectivity state READY and the health listener is disabled. Transitioning SubConn to READY.", sd.subConn)
}
sd.effectiveState = connectivity.Ready
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Ready,
Picker: &picker{result: balancer.PickResult{SubConn: sd.subConn}},
})
return
}
if b.logger.V(2) {
b.logger.Infof("SubConn %p reported connectivity state READY. Registering health listener.", sd.subConn)
}
// Send a CONNECTING update to take the SubConn out of sticky-TF if
// required.
sd.effectiveState = connectivity.Connecting
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Connecting,
Picker: &picker{err: balancer.ErrNoSubConnAvailable},
})
sd.subConn.RegisterHealthListener(func(scs balancer.SubConnState) {
b.updateSubConnHealthState(sd, scs)
})
return
}
// If the LB policy is READY, and it receives a subchannel state change,
// it means that the READY subchannel has failed.
// A SubConn can also transition from CONNECTING directly to IDLE when
// a transport is successfully created, but the connection fails
// before the SubConn can send the notification for READY. We treat
// this as a successful connection and transition to IDLE.
// TODO: https://github.com/grpc/grpc-go/issues/7862 - Remove the second
// part of the if condition below once the issue is fixed.
if oldState == connectivity.Ready || (oldState == connectivity.Connecting && newState.ConnectivityState == connectivity.Idle) {
// Once a transport fails, the balancer enters IDLE and starts from
// the first address when the picker is used.
b.shutdownRemainingLocked(sd)
sd.effectiveState = newState.ConnectivityState
// READY SubConn interspliced in between CONNECTING and IDLE, need to
// account for that.
if oldState == connectivity.Connecting {
// A known issue (https://github.com/grpc/grpc-go/issues/7862)
// causes a race that prevents the READY state change notification.
// This works around it.
connectionAttemptsSucceededMetric.Record(b.metricsRecorder, 1, b.target)
}
disconnectionsMetric.Record(b.metricsRecorder, 1, b.target)
b.addressList.reset()
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Idle,
Picker: &idlePicker{exitIdle: sync.OnceFunc(b.ExitIdle)},
})
return
}
if b.firstPass {
switch newState.ConnectivityState {
case connectivity.Connecting:
// The effective state can be in either IDLE, CONNECTING or
// TRANSIENT_FAILURE. If it's TRANSIENT_FAILURE, stay in
// TRANSIENT_FAILURE until it's READY. See A62.
if sd.effectiveState != connectivity.TransientFailure {
sd.effectiveState = connectivity.Connecting
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Connecting,
Picker: &picker{err: balancer.ErrNoSubConnAvailable},
})
}
case connectivity.TransientFailure:
sd.lastErr = newState.ConnectionError
sd.effectiveState = connectivity.TransientFailure
// Since we're re-using common SubConns while handling resolver
// updates, we could receive an out of turn TRANSIENT_FAILURE from
// a pass over the previous address list. Happy Eyeballs will also
// cause out of order updates to arrive.
if curAddr := b.addressList.currentAddress(); equalAddressIgnoringBalAttributes(&curAddr, &sd.addr) {
b.cancelConnectionTimer()
if b.addressList.increment() {
b.requestConnectionLocked()
return
}
}
// End the first pass if we've seen a TRANSIENT_FAILURE from all
// SubConns once.
b.endFirstPassIfPossibleLocked(newState.ConnectionError)
}
return
}
// We have finished the first pass, keep re-connecting failing SubConns.
switch newState.ConnectivityState {
case connectivity.TransientFailure:
b.numTF = (b.numTF + 1) % b.subConns.Len()
sd.lastErr = newState.ConnectionError
if b.numTF%b.subConns.Len() == 0 {
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.TransientFailure,
Picker: &picker{err: newState.ConnectionError},
})
}
// We don't need to request re-resolution since the SubConn already
// does that before reporting TRANSIENT_FAILURE.
// TODO: #7534 - Move re-resolution requests from SubConn into
// pick_first.
case connectivity.Idle:
sd.subConn.Connect()
}
}
// endFirstPassIfPossibleLocked ends the first happy-eyeballs pass if all the
// addresses are tried and their SubConns have reported a failure.
func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
// An optimization to avoid iterating over the entire SubConn map.
if b.addressList.isValid() {
return
}
// Connect() has been called on all the SubConns. The first pass can be
// ended if all the SubConns have reported a failure.
for _, sd := range b.subConns.Values() {
if !sd.connectionFailedInFirstPass {
return
}
}
b.firstPass = false
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.TransientFailure,
Picker: &picker{err: lastErr},
})
// Start re-connecting all the SubConns that are already in IDLE.
for _, sd := range b.subConns.Values() {
if sd.rawConnectivityState == connectivity.Idle {
sd.subConn.Connect()
}
}
}
func (b *pickfirstBalancer) isActiveSCData(sd *scData) bool {
activeSD, found := b.subConns.Get(sd.addr)
return found && activeSD == sd
}
func (b *pickfirstBalancer) updateSubConnHealthState(sd *scData, state balancer.SubConnState) {
b.mu.Lock()
defer b.mu.Unlock()
// Previously relevant SubConns can still callback with state updates.
// To prevent pickers from returning these obsolete SubConns, this logic
// is included to check if the current list of active SubConns includes
// this SubConn.
if !b.isActiveSCData(sd) {
return
}
sd.effectiveState = state.ConnectivityState
switch state.ConnectivityState {
case connectivity.Ready:
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Ready,
Picker: &picker{result: balancer.PickResult{SubConn: sd.subConn}},
})
case connectivity.TransientFailure:
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.TransientFailure,
Picker: &picker{err: fmt.Errorf("pickfirst: health check failure: %v", state.ConnectionError)},
})
case connectivity.Connecting:
b.updateBalancerState(balancer.State{
ConnectivityState: connectivity.Connecting,
Picker: &picker{err: balancer.ErrNoSubConnAvailable},
})
default:
b.logger.Errorf("Got unexpected health update for SubConn %p: %v", state)
}
}
// updateBalancerState stores the state reported to the channel and calls
// ClientConn.UpdateState(). As an optimization, it avoids sending duplicate
// updates to the channel.
func (b *pickfirstBalancer) updateBalancerState(newState balancer.State) {
// In case of TransientFailures allow the picker to be updated to update
// the connectivity error, in all other cases don't send duplicate state
// updates.
if newState.ConnectivityState == b.state && b.state != connectivity.TransientFailure {
return
}
b.forceUpdateConcludedStateLocked(newState)
}
// forceUpdateConcludedStateLocked stores the state reported to the channel and
// calls ClientConn.UpdateState().
// A separate function is defined to force update the ClientConn state since the
// channel doesn't correctly assume that LB policies start in CONNECTING and
// relies on LB policy to send an initial CONNECTING update.
func (b *pickfirstBalancer) forceUpdateConcludedStateLocked(newState balancer.State) {
b.state = newState.ConnectivityState
b.cc.UpdateState(newState)
}
type picker struct {
result balancer.PickResult
err error
}
func (p *picker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
return p.result, p.err
}
// idlePicker is used when the SubConn is IDLE and kicks the SubConn into
// CONNECTING when Pick is called.
type idlePicker struct {
exitIdle func()
}
func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
i.exitIdle()
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
}
// addressList manages sequentially iterating over addresses present in a list
// of endpoints. It provides a 1 dimensional view of the addresses present in
// the endpoints.
// This type is not safe for concurrent access.
type addressList struct {
addresses []resolver.Address
idx int
}
func (al *addressList) isValid() bool {
return al.idx < len(al.addresses)
}
func (al *addressList) size() int {
return len(al.addresses)
}
// increment moves to the next index in the address list.
// This method returns false if it went off the list, true otherwise.
func (al *addressList) increment() bool {
if !al.isValid() {
return false
}
al.idx++
return al.idx < len(al.addresses)
}
// currentAddress returns the current address pointed to in the addressList.
// If the list is in an invalid state, it returns an empty address instead.
func (al *addressList) currentAddress() resolver.Address {
if !al.isValid() {
return resolver.Address{}
}
return al.addresses[al.idx]
}
func (al *addressList) reset() {
al.idx = 0
}
func (al *addressList) updateAddrs(addrs []resolver.Address) {
al.addresses = addrs
al.reset()
}
// seekTo returns false if the needle was not found and the current index was
// left unchanged.
func (al *addressList) seekTo(needle resolver.Address) bool {
for ai, addr := range al.addresses {
if !equalAddressIgnoringBalAttributes(&addr, &needle) {
continue
}
al.idx = ai
return true
}
return false
}
// hasNext returns whether incrementing the addressList will result in moving
// past the end of the list. If the list has already moved past the end, it
// returns false.
func (al *addressList) hasNext() bool {
if !al.isValid() {
return false
}
return al.idx+1 < len(al.addresses)
}
// equalAddressIgnoringBalAttributes returns true is a and b are considered
// equal. This is different from the Equal method on the resolver.Address type
// which considers all fields to determine equality. Here, we only consider
// fields that are meaningful to the SubConn.
func equalAddressIgnoringBalAttributes(a, b *resolver.Address) bool {
return a.Addr == b.Addr && a.ServerName == b.ServerName &&
a.Attributes.Equal(b.Attributes)
}

View File

@@ -0,0 +1,72 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package roundrobin defines a roundrobin balancer. Roundrobin balancer is
// installed as one of the default balancers in gRPC, users don't need to
// explicitly install this balancer.
package roundrobin
import (
"fmt"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/endpointsharding"
"google.golang.org/grpc/balancer/pickfirst"
"google.golang.org/grpc/grpclog"
internalgrpclog "google.golang.org/grpc/internal/grpclog"
)
// Name is the name of round_robin balancer.
const Name = "round_robin"
var logger = grpclog.Component("roundrobin")
func init() {
balancer.Register(builder{})
}
type builder struct{}
func (bb builder) Name() string {
return Name
}
func (bb builder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
childBuilder := balancer.Get(pickfirst.Name).Build
bal := &rrBalancer{
cc: cc,
Balancer: endpointsharding.NewBalancer(cc, opts, childBuilder, endpointsharding.Options{}),
}
bal.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[%p] ", bal))
bal.logger.Infof("Created")
return bal
}
type rrBalancer struct {
balancer.Balancer
cc balancer.ClientConn
logger *internalgrpclog.PrefixLogger
}
func (b *rrBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error {
return b.Balancer.UpdateClientConnState(balancer.ClientConnState{
// Enable the health listener in pickfirst children for client side health
// checks and outlier detection, if configured.
ResolverState: pickfirst.EnableHealthListener(ccs.ResolverState),
})
}

134
vendor/google.golang.org/grpc/balancer/subconn.go generated vendored Normal file
View File

@@ -0,0 +1,134 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package balancer
import (
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/resolver"
)
// A SubConn represents a single connection to a gRPC backend service.
//
// All SubConns start in IDLE, and will not try to connect. To trigger a
// connection attempt, Balancers must call Connect.
//
// If the connection attempt fails, the SubConn will transition to
// TRANSIENT_FAILURE for a backoff period, and then return to IDLE. If the
// connection attempt succeeds, it will transition to READY.
//
// If a READY SubConn becomes disconnected, the SubConn will transition to IDLE.
//
// If a connection re-enters IDLE, Balancers must call Connect again to trigger
// a new connection attempt.
//
// Each SubConn contains a list of addresses. gRPC will try to connect to the
// addresses in sequence, and stop trying the remainder once the first
// connection is successful. However, this behavior is deprecated. SubConns
// should only use a single address.
//
// NOTICE: This interface is intended to be implemented by gRPC, or intercepted
// by custom load balancing polices. Users should not need their own complete
// implementation of this interface -- they should always delegate to a SubConn
// returned by ClientConn.NewSubConn() by embedding it in their implementations.
// An embedded SubConn must never be nil, or runtime panics will occur.
type SubConn interface {
// UpdateAddresses updates the addresses used in this SubConn.
// gRPC checks if currently-connected address is still in the new list.
// If it's in the list, the connection will be kept.
// If it's not in the list, the connection will gracefully close, and
// a new connection will be created.
//
// This will trigger a state transition for the SubConn.
//
// Deprecated: this method will be removed. Create new SubConns for new
// addresses instead.
UpdateAddresses([]resolver.Address)
// Connect starts the connecting for this SubConn.
Connect()
// GetOrBuildProducer returns a reference to the existing Producer for this
// ProducerBuilder in this SubConn, or, if one does not currently exist,
// creates a new one and returns it. Returns a close function which may be
// called when the Producer is no longer needed. Otherwise the producer
// will automatically be closed upon connection loss or subchannel close.
// Should only be called on a SubConn in state Ready. Otherwise the
// producer will be unable to create streams.
GetOrBuildProducer(ProducerBuilder) (p Producer, close func())
// Shutdown shuts down the SubConn gracefully. Any started RPCs will be
// allowed to complete. No future calls should be made on the SubConn.
// One final state update will be delivered to the StateListener (or
// UpdateSubConnState; deprecated) with ConnectivityState of Shutdown to
// indicate the shutdown operation. This may be delivered before
// in-progress RPCs are complete and the actual connection is closed.
Shutdown()
// RegisterHealthListener registers a health listener that receives health
// updates for a Ready SubConn. Only one health listener can be registered
// at a time. A health listener should be registered each time the SubConn's
// connectivity state changes to READY. Registering a health listener when
// the connectivity state is not READY may result in undefined behaviour.
// This method must not be called synchronously while handling an update
// from a previously registered health listener.
RegisterHealthListener(func(SubConnState))
// EnforceSubConnEmbedding is included to force implementers to embed
// another implementation of this interface, allowing gRPC to add methods
// without breaking users.
internal.EnforceSubConnEmbedding
}
// A ProducerBuilder is a simple constructor for a Producer. It is used by the
// SubConn to create producers when needed.
type ProducerBuilder interface {
// Build creates a Producer. The first parameter is always a
// grpc.ClientConnInterface (a type to allow creating RPCs/streams on the
// associated SubConn), but is declared as `any` to avoid a dependency
// cycle. Build also returns a close function that will be called when all
// references to the Producer have been given up for a SubConn, or when a
// connectivity state change occurs on the SubConn. The close function
// should always block until all asynchronous cleanup work is completed.
Build(grpcClientConnInterface any) (p Producer, close func())
}
// SubConnState describes the state of a SubConn.
type SubConnState struct {
// ConnectivityState is the connectivity state of the SubConn.
ConnectivityState connectivity.State
// ConnectionError is set if the ConnectivityState is TransientFailure,
// describing the reason the SubConn failed. Otherwise, it is nil.
ConnectionError error
// connectedAddr contains the connected address when ConnectivityState is
// Ready. Otherwise, it is indeterminate.
connectedAddress resolver.Address
}
// connectedAddress returns the connected address for a SubConnState. The
// address is only valid if the state is READY.
func connectedAddress(scs SubConnState) resolver.Address {
return scs.connectedAddress
}
// setConnectedAddress sets the connected address for a SubConnState.
func setConnectedAddress(scs *SubConnState, addr resolver.Address) {
scs.connectedAddress = addr
}
// A Producer is a type shared among potentially many consumers. It is
// associated with a SubConn, and an implementation will typically contain
// other methods to provide additional functionality, e.g. configuration or
// subscription registration.
type Producer any

521
vendor/google.golang.org/grpc/balancer_wrapper.go generated vendored Normal file
View File

@@ -0,0 +1,521 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpc
import (
"context"
"fmt"
"sync"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/balancer/gracefulswitch"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/status"
)
var (
setConnectedAddress = internal.SetConnectedAddress.(func(*balancer.SubConnState, resolver.Address))
// noOpRegisterHealthListenerFn is used when client side health checking is
// disabled. It sends a single READY update on the registered listener.
noOpRegisterHealthListenerFn = func(_ context.Context, listener func(balancer.SubConnState)) func() {
listener(balancer.SubConnState{ConnectivityState: connectivity.Ready})
return func() {}
}
)
// ccBalancerWrapper sits between the ClientConn and the Balancer.
//
// ccBalancerWrapper implements methods corresponding to the ones on the
// balancer.Balancer interface. The ClientConn is free to call these methods
// concurrently and the ccBalancerWrapper ensures that calls from the ClientConn
// to the Balancer happen in order by performing them in the serializer, without
// any mutexes held.
//
// ccBalancerWrapper also implements the balancer.ClientConn interface and is
// passed to the Balancer implementations. It invokes unexported methods on the
// ClientConn to handle these calls from the Balancer.
//
// It uses the gracefulswitch.Balancer internally to ensure that balancer
// switches happen in a graceful manner.
type ccBalancerWrapper struct {
internal.EnforceClientConnEmbedding
// The following fields are initialized when the wrapper is created and are
// read-only afterwards, and therefore can be accessed without a mutex.
cc *ClientConn
opts balancer.BuildOptions
serializer *grpcsync.CallbackSerializer
serializerCancel context.CancelFunc
// The following fields are only accessed within the serializer or during
// initialization.
curBalancerName string
balancer *gracefulswitch.Balancer
// The following field is protected by mu. Caller must take cc.mu before
// taking mu.
mu sync.Mutex
closed bool
}
// newCCBalancerWrapper creates a new balancer wrapper in idle state. The
// underlying balancer is not created until the updateClientConnState() method
// is invoked.
func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper {
ctx, cancel := context.WithCancel(cc.ctx)
ccb := &ccBalancerWrapper{
cc: cc,
opts: balancer.BuildOptions{
DialCreds: cc.dopts.copts.TransportCredentials,
CredsBundle: cc.dopts.copts.CredsBundle,
Dialer: cc.dopts.copts.Dialer,
Authority: cc.authority,
CustomUserAgent: cc.dopts.copts.UserAgent,
ChannelzParent: cc.channelz,
Target: cc.parsedTarget,
},
serializer: grpcsync.NewCallbackSerializer(ctx),
serializerCancel: cancel,
}
ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts)
return ccb
}
func (ccb *ccBalancerWrapper) MetricsRecorder() stats.MetricsRecorder {
return ccb.cc.metricsRecorderList
}
// updateClientConnState is invoked by grpc to push a ClientConnState update to
// the underlying balancer. This is always executed from the serializer, so
// it is safe to call into the balancer here.
func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error {
errCh := make(chan error)
uccs := func(ctx context.Context) {
defer close(errCh)
if ctx.Err() != nil || ccb.balancer == nil {
return
}
name := gracefulswitch.ChildName(ccs.BalancerConfig)
if ccb.curBalancerName != name {
ccb.curBalancerName = name
channelz.Infof(logger, ccb.cc.channelz, "Channel switches to new LB policy %q", name)
}
err := ccb.balancer.UpdateClientConnState(*ccs)
if logger.V(2) && err != nil {
logger.Infof("error from balancer.UpdateClientConnState: %v", err)
}
errCh <- err
}
onFailure := func() { close(errCh) }
// UpdateClientConnState can race with Close, and when the latter wins, the
// serializer is closed, and the attempt to schedule the callback will fail.
// It is acceptable to ignore this failure. But since we want to handle the
// state update in a blocking fashion (when we successfully schedule the
// callback), we have to use the ScheduleOr method and not the MaybeSchedule
// method on the serializer.
ccb.serializer.ScheduleOr(uccs, onFailure)
return <-errCh
}
// resolverError is invoked by grpc to push a resolver error to the underlying
// balancer. The call to the balancer is executed from the serializer.
func (ccb *ccBalancerWrapper) resolverError(err error) {
ccb.serializer.TrySchedule(func(ctx context.Context) {
if ctx.Err() != nil || ccb.balancer == nil {
return
}
ccb.balancer.ResolverError(err)
})
}
// close initiates async shutdown of the wrapper. cc.mu must be held when
// calling this function. To determine the wrapper has finished shutting down,
// the channel should block on ccb.serializer.Done() without cc.mu held.
func (ccb *ccBalancerWrapper) close() {
ccb.mu.Lock()
ccb.closed = true
ccb.mu.Unlock()
channelz.Info(logger, ccb.cc.channelz, "ccBalancerWrapper: closing")
ccb.serializer.TrySchedule(func(context.Context) {
if ccb.balancer == nil {
return
}
ccb.balancer.Close()
ccb.balancer = nil
})
ccb.serializerCancel()
}
// exitIdle invokes the balancer's exitIdle method in the serializer.
func (ccb *ccBalancerWrapper) exitIdle() {
ccb.serializer.TrySchedule(func(ctx context.Context) {
if ctx.Err() != nil || ccb.balancer == nil {
return
}
ccb.balancer.ExitIdle()
})
}
func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
ccb.cc.mu.Lock()
defer ccb.cc.mu.Unlock()
ccb.mu.Lock()
if ccb.closed {
ccb.mu.Unlock()
return nil, fmt.Errorf("balancer is being closed; no new SubConns allowed")
}
ccb.mu.Unlock()
if len(addrs) == 0 {
return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
}
ac, err := ccb.cc.newAddrConnLocked(addrs, opts)
if err != nil {
channelz.Warningf(logger, ccb.cc.channelz, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err)
return nil, err
}
acbw := &acBalancerWrapper{
ccb: ccb,
ac: ac,
producers: make(map[balancer.ProducerBuilder]*refCountedProducer),
stateListener: opts.StateListener,
healthData: newHealthData(connectivity.Idle),
}
ac.acbw = acbw
return acbw, nil
}
func (ccb *ccBalancerWrapper) RemoveSubConn(balancer.SubConn) {
// The graceful switch balancer will never call this.
logger.Errorf("ccb RemoveSubConn(%v) called unexpectedly, sc")
}
func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
acbw, ok := sc.(*acBalancerWrapper)
if !ok {
return
}
acbw.UpdateAddresses(addrs)
}
func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
ccb.cc.mu.Lock()
defer ccb.cc.mu.Unlock()
if ccb.cc.conns == nil {
// The CC has been closed; ignore this update.
return
}
ccb.mu.Lock()
if ccb.closed {
ccb.mu.Unlock()
return
}
ccb.mu.Unlock()
// Update picker before updating state. Even though the ordering here does
// not matter, it can lead to multiple calls of Pick in the common start-up
// case where we wait for ready and then perform an RPC. If the picker is
// updated later, we could call the "connecting" picker when the state is
// updated, and then call the "ready" picker after the picker gets updated.
// Note that there is no need to check if the balancer wrapper was closed,
// as we know the graceful switch LB policy will not call cc if it has been
// closed.
ccb.cc.pickerWrapper.updatePicker(s.Picker)
ccb.cc.csMgr.updateState(s.ConnectivityState)
}
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) {
ccb.cc.mu.RLock()
defer ccb.cc.mu.RUnlock()
ccb.mu.Lock()
if ccb.closed {
ccb.mu.Unlock()
return
}
ccb.mu.Unlock()
ccb.cc.resolveNowLocked(o)
}
func (ccb *ccBalancerWrapper) Target() string {
return ccb.cc.target
}
// acBalancerWrapper is a wrapper on top of ac for balancers.
// It implements balancer.SubConn interface.
type acBalancerWrapper struct {
internal.EnforceSubConnEmbedding
ac *addrConn // read-only
ccb *ccBalancerWrapper // read-only
stateListener func(balancer.SubConnState)
producersMu sync.Mutex
producers map[balancer.ProducerBuilder]*refCountedProducer
// Access to healthData is protected by healthMu.
healthMu sync.Mutex
// healthData is stored as a pointer to detect when the health listener is
// dropped or updated. This is required as closures can't be compared for
// equality.
healthData *healthData
}
// healthData holds data related to health state reporting.
type healthData struct {
// connectivityState stores the most recent connectivity state delivered
// to the LB policy. This is stored to avoid sending updates when the
// SubConn has already exited connectivity state READY.
connectivityState connectivity.State
// closeHealthProducer stores function to close the ref counted health
// producer. The health producer is automatically closed when the SubConn
// state changes.
closeHealthProducer func()
}
func newHealthData(s connectivity.State) *healthData {
return &healthData{
connectivityState: s,
closeHealthProducer: func() {},
}
}
// updateState is invoked by grpc to push a subConn state update to the
// underlying balancer.
func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolver.Address, err error) {
acbw.ccb.serializer.TrySchedule(func(ctx context.Context) {
if ctx.Err() != nil || acbw.ccb.balancer == nil {
return
}
// Invalidate all producers on any state change.
acbw.closeProducers()
// Even though it is optional for balancers, gracefulswitch ensures
// opts.StateListener is set, so this cannot ever be nil.
// TODO: delete this comment when UpdateSubConnState is removed.
scs := balancer.SubConnState{ConnectivityState: s, ConnectionError: err}
if s == connectivity.Ready {
setConnectedAddress(&scs, curAddr)
}
// Invalidate the health listener by updating the healthData.
acbw.healthMu.Lock()
// A race may occur if a health listener is registered soon after the
// connectivity state is set but before the stateListener is called.
// Two cases may arise:
// 1. The new state is not READY: RegisterHealthListener has checks to
// ensure no updates are sent when the connectivity state is not
// READY.
// 2. The new state is READY: This means that the old state wasn't Ready.
// The RegisterHealthListener API mentions that a health listener
// must not be registered when a SubConn is not ready to avoid such
// races. When this happens, the LB policy would get health updates
// on the old listener. When the LB policy registers a new listener
// on receiving the connectivity update, the health updates will be
// sent to the new health listener.
acbw.healthData = newHealthData(scs.ConnectivityState)
acbw.healthMu.Unlock()
acbw.stateListener(scs)
})
}
func (acbw *acBalancerWrapper) String() string {
return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelz.ID)
}
func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
acbw.ac.updateAddrs(addrs)
}
func (acbw *acBalancerWrapper) Connect() {
go acbw.ac.connect()
}
func (acbw *acBalancerWrapper) Shutdown() {
acbw.closeProducers()
acbw.ccb.cc.removeAddrConn(acbw.ac, errConnDrain)
}
// NewStream begins a streaming RPC on the addrConn. If the addrConn is not
// ready, blocks until it is or ctx expires. Returns an error when the context
// expires or the addrConn is shut down.
func (acbw *acBalancerWrapper) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) {
transport := acbw.ac.getReadyTransport()
if transport == nil {
return nil, status.Errorf(codes.Unavailable, "SubConn state is not Ready")
}
return newNonRetryClientStream(ctx, desc, method, transport, acbw.ac, opts...)
}
// Invoke performs a unary RPC. If the addrConn is not ready, returns
// errSubConnNotReady.
func (acbw *acBalancerWrapper) Invoke(ctx context.Context, method string, args any, reply any, opts ...CallOption) error {
cs, err := acbw.NewStream(ctx, unaryStreamDesc, method, opts...)
if err != nil {
return err
}
if err := cs.SendMsg(args); err != nil {
return err
}
return cs.RecvMsg(reply)
}
type refCountedProducer struct {
producer balancer.Producer
refs int // number of current refs to the producer
close func() // underlying producer's close function
}
func (acbw *acBalancerWrapper) GetOrBuildProducer(pb balancer.ProducerBuilder) (balancer.Producer, func()) {
acbw.producersMu.Lock()
defer acbw.producersMu.Unlock()
// Look up existing producer from this builder.
pData := acbw.producers[pb]
if pData == nil {
// Not found; create a new one and add it to the producers map.
p, closeFn := pb.Build(acbw)
pData = &refCountedProducer{producer: p, close: closeFn}
acbw.producers[pb] = pData
}
// Account for this new reference.
pData.refs++
// Return a cleanup function wrapped in a OnceFunc to remove this reference
// and delete the refCountedProducer from the map if the total reference
// count goes to zero.
unref := func() {
acbw.producersMu.Lock()
// If closeProducers has already closed this producer instance, refs is
// set to 0, so the check after decrementing will never pass, and the
// producer will not be double-closed.
pData.refs--
if pData.refs == 0 {
defer pData.close() // Run outside the acbw mutex
delete(acbw.producers, pb)
}
acbw.producersMu.Unlock()
}
return pData.producer, sync.OnceFunc(unref)
}
func (acbw *acBalancerWrapper) closeProducers() {
acbw.producersMu.Lock()
defer acbw.producersMu.Unlock()
for pb, pData := range acbw.producers {
pData.refs = 0
pData.close()
delete(acbw.producers, pb)
}
}
// healthProducerRegisterFn is a type alias for the health producer's function
// for registering listeners.
type healthProducerRegisterFn = func(context.Context, balancer.SubConn, string, func(balancer.SubConnState)) func()
// healthListenerRegFn returns a function to register a listener for health
// updates. If client side health checks are disabled, the registered listener
// will get a single READY (raw connectivity state) update.
//
// Client side health checking is enabled when all the following
// conditions are satisfied:
// 1. Health checking is not disabled using the dial option.
// 2. The health package is imported.
// 3. The health check config is present in the service config.
func (acbw *acBalancerWrapper) healthListenerRegFn() func(context.Context, func(balancer.SubConnState)) func() {
if acbw.ccb.cc.dopts.disableHealthCheck {
return noOpRegisterHealthListenerFn
}
cfg := acbw.ac.cc.healthCheckConfig()
if cfg == nil {
return noOpRegisterHealthListenerFn
}
regHealthLisFn := internal.RegisterClientHealthCheckListener
if regHealthLisFn == nil {
// The health package is not imported.
channelz.Error(logger, acbw.ac.channelz, "Health check is requested but health package is not imported.")
return noOpRegisterHealthListenerFn
}
return func(ctx context.Context, listener func(balancer.SubConnState)) func() {
return regHealthLisFn.(healthProducerRegisterFn)(ctx, acbw, cfg.ServiceName, listener)
}
}
// RegisterHealthListener accepts a health listener from the LB policy. It sends
// updates to the health listener as long as the SubConn's connectivity state
// doesn't change and a new health listener is not registered. To invalidate
// the currently registered health listener, acbw updates the healthData. If a
// nil listener is registered, the active health listener is dropped.
func (acbw *acBalancerWrapper) RegisterHealthListener(listener func(balancer.SubConnState)) {
acbw.healthMu.Lock()
defer acbw.healthMu.Unlock()
acbw.healthData.closeHealthProducer()
// listeners should not be registered when the connectivity state
// isn't Ready. This may happen when the balancer registers a listener
// after the connectivityState is updated, but before it is notified
// of the update.
if acbw.healthData.connectivityState != connectivity.Ready {
return
}
// Replace the health data to stop sending updates to any previously
// registered health listeners.
hd := newHealthData(connectivity.Ready)
acbw.healthData = hd
if listener == nil {
return
}
registerFn := acbw.healthListenerRegFn()
acbw.ccb.serializer.TrySchedule(func(ctx context.Context) {
if ctx.Err() != nil || acbw.ccb.balancer == nil {
return
}
// Don't send updates if a new listener is registered.
acbw.healthMu.Lock()
defer acbw.healthMu.Unlock()
if acbw.healthData != hd {
return
}
// Serialize the health updates from the health producer with
// other calls into the LB policy.
listenerWrapper := func(scs balancer.SubConnState) {
acbw.ccb.serializer.TrySchedule(func(ctx context.Context) {
if ctx.Err() != nil || acbw.ccb.balancer == nil {
return
}
acbw.healthMu.Lock()
defer acbw.healthMu.Unlock()
if acbw.healthData != hd {
return
}
listener(scs)
})
}
hd.closeHealthProducer = registerFn(ctx, listenerWrapper)
})
}

File diff suppressed because it is too large Load Diff

74
vendor/google.golang.org/grpc/call.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
/*
*
* Copyright 2014 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpc
import (
"context"
)
// Invoke sends the RPC request on the wire and returns after response is
// received. This is typically called by generated code.
//
// All errors returned by Invoke are compatible with the status package.
func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply any, opts ...CallOption) error {
// allow interceptor to see all applicable call options, which means those
// configured as defaults from dial option as well as per-call options
opts = combine(cc.dopts.callOptions, opts)
if cc.dopts.unaryInt != nil {
return cc.dopts.unaryInt(ctx, method, args, reply, cc, invoke, opts...)
}
return invoke(ctx, method, args, reply, cc, opts...)
}
func combine(o1 []CallOption, o2 []CallOption) []CallOption {
// we don't use append because o1 could have extra capacity whose
// elements would be overwritten, which could cause inadvertent
// sharing (and race conditions) between concurrent calls
if len(o1) == 0 {
return o2
} else if len(o2) == 0 {
return o1
}
ret := make([]CallOption, len(o1)+len(o2))
copy(ret, o1)
copy(ret[len(o1):], o2)
return ret
}
// Invoke sends the RPC request on the wire and returns after response is
// received. This is typically called by generated code.
//
// DEPRECATED: Use ClientConn.Invoke instead.
func Invoke(ctx context.Context, method string, args, reply any, cc *ClientConn, opts ...CallOption) error {
return cc.Invoke(ctx, method, args, reply, opts...)
}
var unaryStreamDesc = &StreamDesc{ServerStreams: false, ClientStreams: false}
func invoke(ctx context.Context, method string, req, reply any, cc *ClientConn, opts ...CallOption) error {
cs, err := newClientStream(ctx, unaryStreamDesc, cc, method, opts...)
if err != nil {
return err
}
if err := cs.SendMsg(req); err != nil {
return err
}
return cs.RecvMsg(reply)
}

36
vendor/google.golang.org/grpc/channelz/channelz.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package channelz exports internals of the channelz implementation as required
// by other gRPC packages.
//
// The implementation of the channelz spec as defined in
// https://github.com/grpc/proposal/blob/master/A14-channelz.md, is provided by
// the `internal/channelz` package.
//
// # Experimental
//
// Notice: All APIs in this package are experimental and may be removed in a
// later release.
package channelz
import "google.golang.org/grpc/internal/channelz"
// Identifier is an opaque identifier which uniquely identifies an entity in the
// channelz database.
type Identifier = channelz.Identifier

1951
vendor/google.golang.org/grpc/clientconn.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

105
vendor/google.golang.org/grpc/codec.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
/*
*
* Copyright 2014 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpc
import (
"google.golang.org/grpc/encoding"
_ "google.golang.org/grpc/encoding/proto" // to register the Codec for "proto"
"google.golang.org/grpc/mem"
)
// baseCodec captures the new encoding.CodecV2 interface without the Name
// function, allowing it to be implemented by older Codec and encoding.Codec
// implementations. The omitted Name function is only needed for the register in
// the encoding package and is not part of the core functionality.
type baseCodec interface {
Marshal(v any) (mem.BufferSlice, error)
Unmarshal(data mem.BufferSlice, v any) error
}
// getCodec returns an encoding.CodecV2 for the codec of the given name (if
// registered). Initially checks the V2 registry with encoding.GetCodecV2 and
// returns the V2 codec if it is registered. Otherwise, it checks the V1 registry
// with encoding.GetCodec and if it is registered wraps it with newCodecV1Bridge
// to turn it into an encoding.CodecV2. Returns nil otherwise.
func getCodec(name string) encoding.CodecV2 {
if codecV1 := encoding.GetCodec(name); codecV1 != nil {
return newCodecV1Bridge(codecV1)
}
return encoding.GetCodecV2(name)
}
func newCodecV0Bridge(c Codec) baseCodec {
return codecV0Bridge{codec: c}
}
func newCodecV1Bridge(c encoding.Codec) encoding.CodecV2 {
return codecV1Bridge{
codecV0Bridge: codecV0Bridge{codec: c},
name: c.Name(),
}
}
var _ baseCodec = codecV0Bridge{}
type codecV0Bridge struct {
codec interface {
Marshal(v any) ([]byte, error)
Unmarshal(data []byte, v any) error
}
}
func (c codecV0Bridge) Marshal(v any) (mem.BufferSlice, error) {
data, err := c.codec.Marshal(v)
if err != nil {
return nil, err
}
return mem.BufferSlice{mem.SliceBuffer(data)}, nil
}
func (c codecV0Bridge) Unmarshal(data mem.BufferSlice, v any) (err error) {
return c.codec.Unmarshal(data.Materialize(), v)
}
var _ encoding.CodecV2 = codecV1Bridge{}
type codecV1Bridge struct {
codecV0Bridge
name string
}
func (c codecV1Bridge) Name() string {
return c.name
}
// Codec defines the interface gRPC uses to encode and decode messages.
// Note that implementations of this interface must be thread safe;
// a Codec's methods can be called from concurrent goroutines.
//
// Deprecated: use encoding.Codec instead.
type Codec interface {
// Marshal returns the wire format of v.
Marshal(v any) ([]byte, error)
// Unmarshal parses the wire format into v.
Unmarshal(data []byte, v any) error
// String returns the name of the Codec implementation. This is unused by
// gRPC.
String() string
}

111
vendor/google.golang.org/grpc/codes/code_string.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package codes
import (
"strconv"
"google.golang.org/grpc/internal"
)
func init() {
internal.CanonicalString = canonicalString
}
func (c Code) String() string {
switch c {
case OK:
return "OK"
case Canceled:
return "Canceled"
case Unknown:
return "Unknown"
case InvalidArgument:
return "InvalidArgument"
case DeadlineExceeded:
return "DeadlineExceeded"
case NotFound:
return "NotFound"
case AlreadyExists:
return "AlreadyExists"
case PermissionDenied:
return "PermissionDenied"
case ResourceExhausted:
return "ResourceExhausted"
case FailedPrecondition:
return "FailedPrecondition"
case Aborted:
return "Aborted"
case OutOfRange:
return "OutOfRange"
case Unimplemented:
return "Unimplemented"
case Internal:
return "Internal"
case Unavailable:
return "Unavailable"
case DataLoss:
return "DataLoss"
case Unauthenticated:
return "Unauthenticated"
default:
return "Code(" + strconv.FormatInt(int64(c), 10) + ")"
}
}
func canonicalString(c Code) string {
switch c {
case OK:
return "OK"
case Canceled:
return "CANCELLED"
case Unknown:
return "UNKNOWN"
case InvalidArgument:
return "INVALID_ARGUMENT"
case DeadlineExceeded:
return "DEADLINE_EXCEEDED"
case NotFound:
return "NOT_FOUND"
case AlreadyExists:
return "ALREADY_EXISTS"
case PermissionDenied:
return "PERMISSION_DENIED"
case ResourceExhausted:
return "RESOURCE_EXHAUSTED"
case FailedPrecondition:
return "FAILED_PRECONDITION"
case Aborted:
return "ABORTED"
case OutOfRange:
return "OUT_OF_RANGE"
case Unimplemented:
return "UNIMPLEMENTED"
case Internal:
return "INTERNAL"
case Unavailable:
return "UNAVAILABLE"
case DataLoss:
return "DATA_LOSS"
case Unauthenticated:
return "UNAUTHENTICATED"
default:
return "CODE(" + strconv.FormatInt(int64(c), 10) + ")"
}
}

250
vendor/google.golang.org/grpc/codes/codes.go generated vendored Normal file
View File

@@ -0,0 +1,250 @@
/*
*
* Copyright 2014 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package codes defines the canonical error codes used by gRPC. It is
// consistent across various languages.
package codes // import "google.golang.org/grpc/codes"
import (
"fmt"
"strconv"
)
// A Code is a status code defined according to the [gRPC documentation].
//
// Only the codes defined as consts in this package are valid codes. Do not use
// other code values. Behavior of other codes is implementation-specific and
// interoperability between implementations is not guaranteed.
//
// [gRPC documentation]: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
type Code uint32
const (
// OK is returned on success.
OK Code = 0
// Canceled indicates the operation was canceled (typically by the caller).
//
// The gRPC framework will generate this error code when cancellation
// is requested.
Canceled Code = 1
// Unknown error. An example of where this error may be returned is
// if a Status value received from another address space belongs to
// an error-space that is not known in this address space. Also
// errors raised by APIs that do not return enough error information
// may be converted to this error.
//
// The gRPC framework will generate this error code in the above two
// mentioned cases.
Unknown Code = 2
// InvalidArgument indicates client specified an invalid argument.
// Note that this differs from FailedPrecondition. It indicates arguments
// that are problematic regardless of the state of the system
// (e.g., a malformed file name).
//
// This error code will not be generated by the gRPC framework.
InvalidArgument Code = 3
// DeadlineExceeded means operation expired before completion.
// For operations that change the state of the system, this error may be
// returned even if the operation has completed successfully. For
// example, a successful response from a server could have been delayed
// long enough for the deadline to expire.
//
// The gRPC framework will generate this error code when the deadline is
// exceeded.
DeadlineExceeded Code = 4
// NotFound means some requested entity (e.g., file or directory) was
// not found.
//
// This error code will not be generated by the gRPC framework.
NotFound Code = 5
// AlreadyExists means an attempt to create an entity failed because one
// already exists.
//
// This error code will not be generated by the gRPC framework.
AlreadyExists Code = 6
// PermissionDenied indicates the caller does not have permission to
// execute the specified operation. It must not be used for rejections
// caused by exhausting some resource (use ResourceExhausted
// instead for those errors). It must not be
// used if the caller cannot be identified (use Unauthenticated
// instead for those errors).
//
// This error code will not be generated by the gRPC core framework,
// but expect authentication middleware to use it.
PermissionDenied Code = 7
// ResourceExhausted indicates some resource has been exhausted, perhaps
// a per-user quota, or perhaps the entire file system is out of space.
//
// This error code will be generated by the gRPC framework in
// out-of-memory and server overload situations, or when a message is
// larger than the configured maximum size.
ResourceExhausted Code = 8
// FailedPrecondition indicates operation was rejected because the
// system is not in a state required for the operation's execution.
// For example, directory to be deleted may be non-empty, an rmdir
// operation is applied to a non-directory, etc.
//
// A litmus test that may help a service implementor in deciding
// between FailedPrecondition, Aborted, and Unavailable:
// (a) Use Unavailable if the client can retry just the failing call.
// (b) Use Aborted if the client should retry at a higher-level
// (e.g., restarting a read-modify-write sequence).
// (c) Use FailedPrecondition if the client should not retry until
// the system state has been explicitly fixed. E.g., if an "rmdir"
// fails because the directory is non-empty, FailedPrecondition
// should be returned since the client should not retry unless
// they have first fixed up the directory by deleting files from it.
// (d) Use FailedPrecondition if the client performs conditional
// REST Get/Update/Delete on a resource and the resource on the
// server does not match the condition. E.g., conflicting
// read-modify-write on the same resource.
//
// This error code will not be generated by the gRPC framework.
FailedPrecondition Code = 9
// Aborted indicates the operation was aborted, typically due to a
// concurrency issue like sequencer check failures, transaction aborts,
// etc.
//
// See litmus test above for deciding between FailedPrecondition,
// Aborted, and Unavailable.
//
// This error code will not be generated by the gRPC framework.
Aborted Code = 10
// OutOfRange means operation was attempted past the valid range.
// E.g., seeking or reading past end of file.
//
// Unlike InvalidArgument, this error indicates a problem that may
// be fixed if the system state changes. For example, a 32-bit file
// system will generate InvalidArgument if asked to read at an
// offset that is not in the range [0,2^32-1], but it will generate
// OutOfRange if asked to read from an offset past the current
// file size.
//
// There is a fair bit of overlap between FailedPrecondition and
// OutOfRange. We recommend using OutOfRange (the more specific
// error) when it applies so that callers who are iterating through
// a space can easily look for an OutOfRange error to detect when
// they are done.
//
// This error code will not be generated by the gRPC framework.
OutOfRange Code = 11
// Unimplemented indicates operation is not implemented or not
// supported/enabled in this service.
//
// This error code will be generated by the gRPC framework. Most
// commonly, you will see this error code when a method implementation
// is missing on the server. It can also be generated for unknown
// compression algorithms or a disagreement as to whether an RPC should
// be streaming.
Unimplemented Code = 12
// Internal errors. Means some invariants expected by underlying
// system has been broken. If you see one of these errors,
// something is very broken.
//
// This error code will be generated by the gRPC framework in several
// internal error conditions.
Internal Code = 13
// Unavailable indicates the service is currently unavailable.
// This is a most likely a transient condition and may be corrected
// by retrying with a backoff. Note that it is not always safe to retry
// non-idempotent operations.
//
// See litmus test above for deciding between FailedPrecondition,
// Aborted, and Unavailable.
//
// This error code will be generated by the gRPC framework during
// abrupt shutdown of a server process or network connection.
Unavailable Code = 14
// DataLoss indicates unrecoverable data loss or corruption.
//
// This error code will not be generated by the gRPC framework.
DataLoss Code = 15
// Unauthenticated indicates the request does not have valid
// authentication credentials for the operation.
//
// The gRPC framework will generate this error code when the
// authentication metadata is invalid or a Credentials callback fails,
// but also expect authentication middleware to generate it.
Unauthenticated Code = 16
_maxCode = 17
)
var strToCode = map[string]Code{
`"OK"`: OK,
`"CANCELLED"`:/* [sic] */ Canceled,
`"UNKNOWN"`: Unknown,
`"INVALID_ARGUMENT"`: InvalidArgument,
`"DEADLINE_EXCEEDED"`: DeadlineExceeded,
`"NOT_FOUND"`: NotFound,
`"ALREADY_EXISTS"`: AlreadyExists,
`"PERMISSION_DENIED"`: PermissionDenied,
`"RESOURCE_EXHAUSTED"`: ResourceExhausted,
`"FAILED_PRECONDITION"`: FailedPrecondition,
`"ABORTED"`: Aborted,
`"OUT_OF_RANGE"`: OutOfRange,
`"UNIMPLEMENTED"`: Unimplemented,
`"INTERNAL"`: Internal,
`"UNAVAILABLE"`: Unavailable,
`"DATA_LOSS"`: DataLoss,
`"UNAUTHENTICATED"`: Unauthenticated,
}
// UnmarshalJSON unmarshals b into the Code.
func (c *Code) UnmarshalJSON(b []byte) error {
// From json.Unmarshaler: By convention, to approximate the behavior of
// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
// a no-op.
if string(b) == "null" {
return nil
}
if c == nil {
return fmt.Errorf("nil receiver passed to UnmarshalJSON")
}
if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil {
if ci >= _maxCode {
return fmt.Errorf("invalid code: %d", ci)
}
*c = Code(ci)
return nil
}
if jc, ok := strToCode[string(b)]; ok {
*c = jc
return nil
}
return fmt.Errorf("invalid code: %q", string(b))
}

View File

@@ -0,0 +1,94 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package connectivity defines connectivity semantics.
// For details, see https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md.
package connectivity
import (
"google.golang.org/grpc/grpclog"
)
var logger = grpclog.Component("core")
// State indicates the state of connectivity.
// It can be the state of a ClientConn or SubConn.
type State int
func (s State) String() string {
switch s {
case Idle:
return "IDLE"
case Connecting:
return "CONNECTING"
case Ready:
return "READY"
case TransientFailure:
return "TRANSIENT_FAILURE"
case Shutdown:
return "SHUTDOWN"
default:
logger.Errorf("unknown connectivity state: %d", s)
return "INVALID_STATE"
}
}
const (
// Idle indicates the ClientConn is idle.
Idle State = iota
// Connecting indicates the ClientConn is connecting.
Connecting
// Ready indicates the ClientConn is ready for work.
Ready
// TransientFailure indicates the ClientConn has seen a failure but expects to recover.
TransientFailure
// Shutdown indicates the ClientConn has started shutting down.
Shutdown
)
// ServingMode indicates the current mode of operation of the server.
//
// Only xDS enabled gRPC servers currently report their serving mode.
type ServingMode int
const (
// ServingModeStarting indicates that the server is starting up.
ServingModeStarting ServingMode = iota
// ServingModeServing indicates that the server contains all required
// configuration and is serving RPCs.
ServingModeServing
// ServingModeNotServing indicates that the server is not accepting new
// connections. Existing connections will be closed gracefully, allowing
// in-progress RPCs to complete. A server enters this mode when it does not
// contain the required configuration to serve RPCs.
ServingModeNotServing
)
func (s ServingMode) String() string {
switch s {
case ServingModeStarting:
return "STARTING"
case ServingModeServing:
return "SERVING"
case ServingModeNotServing:
return "NOT_SERVING"
default:
logger.Errorf("unknown serving mode: %d", s)
return "INVALID_MODE"
}
}

View File

@@ -0,0 +1,337 @@
/*
*
* Copyright 2014 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package credentials implements various credentials supported by gRPC library,
// which encapsulate all the state needed by a client to authenticate with a
// server and make various assertions, e.g., about the client's identity, role,
// or whether it is authorized to make a particular call.
package credentials // import "google.golang.org/grpc/credentials"
import (
"context"
"errors"
"fmt"
"net"
"google.golang.org/grpc/attributes"
icredentials "google.golang.org/grpc/internal/credentials"
"google.golang.org/protobuf/proto"
)
// PerRPCCredentials defines the common interface for the credentials which need to
// attach security information to every RPC (e.g., oauth2).
type PerRPCCredentials interface {
// GetRequestMetadata gets the current request metadata, refreshing tokens
// if required. This should be called by the transport layer on each
// request, and the data should be populated in headers or other
// context. If a status code is returned, it will be used as the status for
// the RPC (restricted to an allowable set of codes as defined by gRFC
// A54). uri is the URI of the entry point for the request. When supported
// by the underlying implementation, ctx can be used for timeout and
// cancellation. Additionally, RequestInfo data will be available via ctx
// to this call.
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
// RequireTransportSecurity indicates whether the credentials requires
// transport security.
RequireTransportSecurity() bool
}
// SecurityLevel defines the protection level on an established connection.
//
// This API is experimental.
type SecurityLevel int
const (
// InvalidSecurityLevel indicates an invalid security level.
// The zero SecurityLevel value is invalid for backward compatibility.
InvalidSecurityLevel SecurityLevel = iota
// NoSecurity indicates a connection is insecure.
NoSecurity
// IntegrityOnly indicates a connection only provides integrity protection.
IntegrityOnly
// PrivacyAndIntegrity indicates a connection provides both privacy and integrity protection.
PrivacyAndIntegrity
)
// String returns SecurityLevel in a string format.
func (s SecurityLevel) String() string {
switch s {
case NoSecurity:
return "NoSecurity"
case IntegrityOnly:
return "IntegrityOnly"
case PrivacyAndIntegrity:
return "PrivacyAndIntegrity"
}
return fmt.Sprintf("invalid SecurityLevel: %v", int(s))
}
// CommonAuthInfo contains authenticated information common to AuthInfo implementations.
// It should be embedded in a struct implementing AuthInfo to provide additional information
// about the credentials.
//
// This API is experimental.
type CommonAuthInfo struct {
SecurityLevel SecurityLevel
}
// GetCommonAuthInfo returns the pointer to CommonAuthInfo struct.
func (c CommonAuthInfo) GetCommonAuthInfo() CommonAuthInfo {
return c
}
// ProtocolInfo provides static information regarding transport credentials.
type ProtocolInfo struct {
// ProtocolVersion is the gRPC wire protocol version.
//
// Deprecated: this is unused by gRPC.
ProtocolVersion string
// SecurityProtocol is the security protocol in use.
SecurityProtocol string
// SecurityVersion is the security protocol version. It is a static version string from the
// credentials, not a value that reflects per-connection protocol negotiation. To retrieve
// details about the credentials used for a connection, use the Peer's AuthInfo field instead.
//
// Deprecated: please use Peer.AuthInfo.
SecurityVersion string
// ServerName is the user-configured server name. If set, this overrides
// the default :authority header used for all RPCs on the channel using the
// containing credentials, unless grpc.WithAuthority is set on the channel,
// in which case that setting will take precedence.
//
// This must be a valid `:authority` header according to
// [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2).
//
// Deprecated: Users should use grpc.WithAuthority to override the authority
// on a channel instead of configuring the credentials.
ServerName string
}
// AuthInfo defines the common interface for the auth information the users are interested in.
// A struct that implements AuthInfo should embed CommonAuthInfo by including additional
// information about the credentials in it.
type AuthInfo interface {
AuthType() string
}
// AuthorityValidator validates the authority used to override the `:authority`
// header. This is an optional interface that implementations of AuthInfo can
// implement if they support per-RPC authority overrides. It is invoked when the
// application attempts to override the HTTP/2 `:authority` header using the
// CallAuthority call option.
type AuthorityValidator interface {
// ValidateAuthority checks the authority value used to override the
// `:authority` header. The authority parameter is the override value
// provided by the application via the CallAuthority option. This value
// typically corresponds to the server hostname or endpoint the RPC is
// targeting. It returns non-nil error if the validation fails.
ValidateAuthority(authority string) error
}
// ErrConnDispatched indicates that rawConn has been dispatched out of gRPC
// and the caller should not close rawConn.
var ErrConnDispatched = errors.New("credentials: rawConn is dispatched out of gRPC")
// TransportCredentials defines the common interface for all the live gRPC wire
// protocols and supported transport security protocols (e.g., TLS, SSL).
type TransportCredentials interface {
// ClientHandshake does the authentication handshake specified by the
// corresponding authentication protocol on rawConn for clients. It returns
// the authenticated connection and the corresponding auth information
// about the connection. The auth information should embed CommonAuthInfo
// to return additional information about the credentials. Implementations
// must use the provided context to implement timely cancellation. gRPC
// will try to reconnect if the error returned is a temporary error
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true). If the
// returned error is a wrapper error, implementations should make sure that
// the error implements Temporary() to have the correct retry behaviors.
// Additionally, ClientHandshakeInfo data will be available via the context
// passed to this call.
//
// The second argument to this method is the `:authority` header value used
// while creating new streams on this connection after authentication
// succeeds. Implementations must use this as the server name during the
// authentication handshake.
//
// If the returned net.Conn is closed, it MUST close the net.Conn provided.
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
// ServerHandshake does the authentication handshake for servers. It returns
// the authenticated connection and the corresponding auth information about
// the connection. The auth information should embed CommonAuthInfo to return additional information
// about the credentials.
//
// If the returned net.Conn is closed, it MUST close the net.Conn provided.
ServerHandshake(net.Conn) (net.Conn, AuthInfo, error)
// Info provides the ProtocolInfo of this TransportCredentials.
Info() ProtocolInfo
// Clone makes a copy of this TransportCredentials.
Clone() TransportCredentials
// OverrideServerName specifies the value used for the following:
//
// - verifying the hostname on the returned certificates
// - as SNI in the client's handshake to support virtual hosting
// - as the value for `:authority` header at stream creation time
//
// The provided string should be a valid `:authority` header according to
// [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2).
//
// Deprecated: this method is unused by gRPC. Users should use
// grpc.WithAuthority to override the authority on a channel instead of
// configuring the credentials.
OverrideServerName(string) error
}
// Bundle is a combination of TransportCredentials and PerRPCCredentials.
//
// It also contains a mode switching method, so it can be used as a combination
// of different credential policies.
//
// Bundle cannot be used together with individual TransportCredentials.
// PerRPCCredentials from Bundle will be appended to other PerRPCCredentials.
//
// This API is experimental.
type Bundle interface {
// TransportCredentials returns the transport credentials from the Bundle.
//
// Implementations must return non-nil transport credentials. If transport
// security is not needed by the Bundle, implementations may choose to
// return insecure.NewCredentials().
TransportCredentials() TransportCredentials
// PerRPCCredentials returns the per-RPC credentials from the Bundle.
//
// May be nil if per-RPC credentials are not needed.
PerRPCCredentials() PerRPCCredentials
// NewWithMode should make a copy of Bundle, and switch mode. Modifying the
// existing Bundle may cause races.
//
// NewWithMode returns nil if the requested mode is not supported.
NewWithMode(mode string) (Bundle, error)
}
// RequestInfo contains request data attached to the context passed to GetRequestMetadata calls.
//
// This API is experimental.
type RequestInfo struct {
// The method passed to Invoke or NewStream for this RPC. (For proto methods, this has the format "/some.Service/Method")
Method string
// AuthInfo contains the information from a security handshake (TransportCredentials.ClientHandshake, TransportCredentials.ServerHandshake)
AuthInfo AuthInfo
}
// requestInfoKey is a struct to be used as the key to store RequestInfo in a
// context.
type requestInfoKey struct{}
// RequestInfoFromContext extracts the RequestInfo from the context if it exists.
//
// This API is experimental.
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
ri, ok = ctx.Value(requestInfoKey{}).(RequestInfo)
return ri, ok
}
// NewContextWithRequestInfo creates a new context from ctx and attaches ri to it.
//
// This RequestInfo will be accessible via RequestInfoFromContext.
//
// Intended to be used from tests for PerRPCCredentials implementations (that
// often need to check connection's SecurityLevel). Should not be used from
// non-test code: the gRPC client already prepares a context with the correct
// RequestInfo attached when calling PerRPCCredentials.GetRequestMetadata.
//
// This API is experimental.
func NewContextWithRequestInfo(ctx context.Context, ri RequestInfo) context.Context {
return context.WithValue(ctx, requestInfoKey{}, ri)
}
// ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes
// it possible to pass arbitrary data to the handshaker from gRPC, resolver,
// balancer etc. Individual credential implementations control the actual
// format of the data that they are willing to receive.
//
// This API is experimental.
type ClientHandshakeInfo struct {
// Attributes contains the attributes for the address. It could be provided
// by the gRPC, resolver, balancer etc.
Attributes *attributes.Attributes
}
// ClientHandshakeInfoFromContext returns the ClientHandshakeInfo struct stored
// in ctx.
//
// This API is experimental.
func ClientHandshakeInfoFromContext(ctx context.Context) ClientHandshakeInfo {
chi, _ := icredentials.ClientHandshakeInfoFromContext(ctx).(ClientHandshakeInfo)
return chi
}
// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
// It returns success if 1) the condition is satisfied or 2) AuthInfo struct does not implement GetCommonAuthInfo() method
// or 3) CommonAuthInfo.SecurityLevel has an invalid zero value. For 2) and 3), it is for the purpose of backward-compatibility.
//
// This API is experimental.
func CheckSecurityLevel(ai AuthInfo, level SecurityLevel) error {
type internalInfo interface {
GetCommonAuthInfo() CommonAuthInfo
}
if ai == nil {
return errors.New("AuthInfo is nil")
}
if ci, ok := ai.(internalInfo); ok {
// CommonAuthInfo.SecurityLevel has an invalid value.
if ci.GetCommonAuthInfo().SecurityLevel == InvalidSecurityLevel {
return nil
}
if ci.GetCommonAuthInfo().SecurityLevel < level {
return fmt.Errorf("requires SecurityLevel %v; connection has %v", level, ci.GetCommonAuthInfo().SecurityLevel)
}
}
// The condition is satisfied or AuthInfo struct does not implement GetCommonAuthInfo() method.
return nil
}
// ChannelzSecurityInfo defines the interface that security protocols should implement
// in order to provide security info to channelz.
//
// This API is experimental.
type ChannelzSecurityInfo interface {
GetSecurityValue() ChannelzSecurityValue
}
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
// and *OtherChannelzSecurityValue.
//
// This API is experimental.
type ChannelzSecurityValue interface {
isChannelzSecurityValue()
}
// OtherChannelzSecurityValue defines the struct that non-TLS protocol should return
// from GetSecurityValue(), which contains protocol specific security info. Note
// the Value field will be sent to users of channelz requesting channel info, and
// thus sensitive info should better be avoided.
//
// This API is experimental.
type OtherChannelzSecurityValue struct {
ChannelzSecurityValue
Name string
Value proto.Message
}

View File

@@ -0,0 +1,104 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package insecure provides an implementation of the
// credentials.TransportCredentials interface which disables transport security.
package insecure
import (
"context"
"net"
"google.golang.org/grpc/credentials"
)
// NewCredentials returns a credentials which disables transport security.
//
// Note that using this credentials with per-RPC credentials which require
// transport security is incompatible and will cause RPCs to fail.
func NewCredentials() credentials.TransportCredentials {
return insecureTC{}
}
// insecureTC implements the insecure transport credentials. The handshake
// methods simply return the passed in net.Conn and set the security level to
// NoSecurity.
type insecureTC struct{}
func (insecureTC) ClientHandshake(_ context.Context, _ string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return conn, info{credentials.CommonAuthInfo{SecurityLevel: credentials.NoSecurity}}, nil
}
func (insecureTC) ServerHandshake(conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return conn, info{credentials.CommonAuthInfo{SecurityLevel: credentials.NoSecurity}}, nil
}
func (insecureTC) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{SecurityProtocol: "insecure"}
}
func (insecureTC) Clone() credentials.TransportCredentials {
return insecureTC{}
}
func (insecureTC) OverrideServerName(string) error {
return nil
}
// info contains the auth information for an insecure connection.
// It implements the AuthInfo interface.
type info struct {
credentials.CommonAuthInfo
}
// AuthType returns the type of info as a string.
func (info) AuthType() string {
return "insecure"
}
// ValidateAuthority allows any value to be overridden for the :authority
// header.
func (info) ValidateAuthority(string) error {
return nil
}
// insecureBundle implements an insecure bundle.
// An insecure bundle provides a thin wrapper around insecureTC to support
// the credentials.Bundle interface.
type insecureBundle struct{}
// NewBundle returns a bundle with disabled transport security and no per rpc credential.
func NewBundle() credentials.Bundle {
return insecureBundle{}
}
// NewWithMode returns a new insecure Bundle. The mode is ignored.
func (insecureBundle) NewWithMode(string) (credentials.Bundle, error) {
return insecureBundle{}, nil
}
// PerRPCCredentials returns an nil implementation as insecure
// bundle does not support a per rpc credential.
func (insecureBundle) PerRPCCredentials() credentials.PerRPCCredentials {
return nil
}
// TransportCredentials returns the underlying insecure transport credential.
func (insecureBundle) TransportCredentials() credentials.TransportCredentials {
return NewCredentials()
}

320
vendor/google.golang.org/grpc/credentials/tls.go generated vendored Normal file
View File

@@ -0,0 +1,320 @@
/*
*
* Copyright 2014 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package credentials
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/url"
"os"
"google.golang.org/grpc/grpclog"
credinternal "google.golang.org/grpc/internal/credentials"
"google.golang.org/grpc/internal/envconfig"
)
const alpnFailureHelpMessage = "If you upgraded from a grpc-go version earlier than 1.67, your TLS connections may have stopped working due to ALPN enforcement. For more details, see: https://github.com/grpc/grpc-go/issues/434"
var logger = grpclog.Component("credentials")
// TLSInfo contains the auth information for a TLS authenticated connection.
// It implements the AuthInfo interface.
type TLSInfo struct {
State tls.ConnectionState
CommonAuthInfo
// This API is experimental.
SPIFFEID *url.URL
}
// AuthType returns the type of TLSInfo as a string.
func (t TLSInfo) AuthType() string {
return "tls"
}
// ValidateAuthority validates the provided authority being used to override the
// :authority header by verifying it against the peer certificates. It returns a
// non-nil error if the validation fails.
func (t TLSInfo) ValidateAuthority(authority string) error {
var errs []error
for _, cert := range t.State.PeerCertificates {
var err error
if err = cert.VerifyHostname(authority); err == nil {
return nil
}
errs = append(errs, err)
}
return fmt.Errorf("credentials: invalid authority %q: %v", authority, errors.Join(errs...))
}
// cipherSuiteLookup returns the string version of a TLS cipher suite ID.
func cipherSuiteLookup(cipherSuiteID uint16) string {
for _, s := range tls.CipherSuites() {
if s.ID == cipherSuiteID {
return s.Name
}
}
for _, s := range tls.InsecureCipherSuites() {
if s.ID == cipherSuiteID {
return s.Name
}
}
return fmt.Sprintf("unknown ID: %v", cipherSuiteID)
}
// GetSecurityValue returns security info requested by channelz.
func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
v := &TLSChannelzSecurityValue{
StandardName: cipherSuiteLookup(t.State.CipherSuite),
}
// Currently there's no way to get LocalCertificate info from tls package.
if len(t.State.PeerCertificates) > 0 {
v.RemoteCertificate = t.State.PeerCertificates[0].Raw
}
return v
}
// tlsCreds is the credentials required for authenticating a connection using TLS.
type tlsCreds struct {
// TLS configuration
config *tls.Config
}
func (c tlsCreds) Info() ProtocolInfo {
return ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
ServerName: c.config.ServerName,
}
}
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
// use local cfg to avoid clobbering ServerName if using multiple endpoints
cfg := credinternal.CloneTLSConfig(c.config)
serverName, _, err := net.SplitHostPort(authority)
if err != nil {
// If the authority had no host port or if the authority cannot be parsed, use it as-is.
serverName = authority
}
cfg.ServerName = serverName
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
errChannel <- conn.Handshake()
close(errChannel)
}()
select {
case err := <-errChannel:
if err != nil {
conn.Close()
return nil, nil, err
}
case <-ctx.Done():
conn.Close()
return nil, nil, ctx.Err()
}
// The negotiated protocol can be either of the following:
// 1. h2: When the server supports ALPN. Only HTTP/2 can be negotiated since
// it is the only protocol advertised by the client during the handshake.
// The tls library ensures that the server chooses a protocol advertised
// by the client.
// 2. "" (empty string): If the server doesn't support ALPN. ALPN is a requirement
// for using HTTP/2 over TLS. We can terminate the connection immediately.
np := conn.ConnectionState().NegotiatedProtocol
if np == "" {
if envconfig.EnforceALPNEnabled {
conn.Close()
return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property. %s", alpnFailureHelpMessage)
}
logger.Warningf("Allowing TLS connection to server %q with ALPN disabled. TLS connections to servers with ALPN disabled will be disallowed in future grpc-go releases", cfg.ServerName)
}
tlsInfo := TLSInfo{
State: conn.ConnectionState(),
CommonAuthInfo: CommonAuthInfo{
SecurityLevel: PrivacyAndIntegrity,
},
}
id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
if id != nil {
tlsInfo.SPIFFEID = id
}
return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
}
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
conn := tls.Server(rawConn, c.config)
if err := conn.Handshake(); err != nil {
conn.Close()
return nil, nil, err
}
cs := conn.ConnectionState()
// The negotiated application protocol can be empty only if the client doesn't
// support ALPN. In such cases, we can close the connection since ALPN is required
// for using HTTP/2 over TLS.
if cs.NegotiatedProtocol == "" {
if envconfig.EnforceALPNEnabled {
conn.Close()
return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property. %s", alpnFailureHelpMessage)
} else if logger.V(2) {
logger.Info("Allowing TLS connection from client with ALPN disabled. TLS connections with ALPN disabled will be disallowed in future grpc-go releases")
}
}
tlsInfo := TLSInfo{
State: cs,
CommonAuthInfo: CommonAuthInfo{
SecurityLevel: PrivacyAndIntegrity,
},
}
id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
if id != nil {
tlsInfo.SPIFFEID = id
}
return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
}
func (c *tlsCreds) Clone() TransportCredentials {
return NewTLS(c.config)
}
func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
c.config.ServerName = serverNameOverride
return nil
}
// The following cipher suites are forbidden for use with HTTP/2 by
// https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
var tls12ForbiddenCipherSuites = map[uint16]struct{}{
tls.TLS_RSA_WITH_AES_128_CBC_SHA: {},
tls.TLS_RSA_WITH_AES_256_CBC_SHA: {},
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: {},
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: {},
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {},
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {},
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: {},
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: {},
}
// NewTLS uses c to construct a TransportCredentials based on TLS.
func NewTLS(c *tls.Config) TransportCredentials {
config := applyDefaults(c)
if config.GetConfigForClient != nil {
oldFn := config.GetConfigForClient
config.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
cfgForClient, err := oldFn(hello)
if err != nil || cfgForClient == nil {
return cfgForClient, err
}
return applyDefaults(cfgForClient), nil
}
}
return &tlsCreds{config: config}
}
func applyDefaults(c *tls.Config) *tls.Config {
config := credinternal.CloneTLSConfig(c)
config.NextProtos = credinternal.AppendH2ToNextProtos(config.NextProtos)
// If the user did not configure a MinVersion and did not configure a
// MaxVersion < 1.2, use MinVersion=1.2, which is required by
// https://datatracker.ietf.org/doc/html/rfc7540#section-9.2
if config.MinVersion == 0 && (config.MaxVersion == 0 || config.MaxVersion >= tls.VersionTLS12) {
config.MinVersion = tls.VersionTLS12
}
// If the user did not configure CipherSuites, use all "secure" cipher
// suites reported by the TLS package, but remove some explicitly forbidden
// by https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
if config.CipherSuites == nil {
for _, cs := range tls.CipherSuites() {
if _, ok := tls12ForbiddenCipherSuites[cs.ID]; !ok {
config.CipherSuites = append(config.CipherSuites, cs.ID)
}
}
}
return config
}
// NewClientTLSFromCert constructs TLS credentials from the provided root
// certificate authority certificate(s) to validate server connections. If
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
//
// serverNameOverride is for testing only. If set to a non empty string, it will
// override the virtual host name of authority (e.g. :authority header field) in
// requests. Users should use grpc.WithAuthority passed to grpc.NewClient to
// override the authority of the client instead.
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
}
// NewClientTLSFromFile constructs TLS credentials from the provided root
// certificate authority certificate file(s) to validate server connections. If
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
//
// serverNameOverride is for testing only. If set to a non empty string, it will
// override the virtual host name of authority (e.g. :authority header field) in
// requests. Users should use grpc.WithAuthority passed to grpc.NewClient to
// override the authority of the client instead.
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
b, err := os.ReadFile(certFile)
if err != nil {
return nil, err
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(b) {
return nil, fmt.Errorf("credentials: failed to append certificates")
}
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
}
// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
}
// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
// from GetSecurityValue(), containing security info like cipher and certificate used.
//
// # Experimental
//
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
// later release.
type TLSChannelzSecurityValue struct {
ChannelzSecurityValue
StandardName string
LocalCertificate []byte
RemoteCertificate []byte
}

797
vendor/google.golang.org/grpc/dialoptions.go generated vendored Normal file
View File

@@ -0,0 +1,797 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpc
import (
"context"
"net"
"net/url"
"time"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/channelz"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal"
internalbackoff "google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/binarylog"
"google.golang.org/grpc/internal/transport"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/mem"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/stats"
)
const (
// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#limits-on-retries-and-hedges
defaultMaxCallAttempts = 5
)
func init() {
internal.AddGlobalDialOptions = func(opt ...DialOption) {
globalDialOptions = append(globalDialOptions, opt...)
}
internal.ClearGlobalDialOptions = func() {
globalDialOptions = nil
}
internal.AddGlobalPerTargetDialOptions = func(opt any) {
if ptdo, ok := opt.(perTargetDialOption); ok {
globalPerTargetDialOptions = append(globalPerTargetDialOptions, ptdo)
}
}
internal.ClearGlobalPerTargetDialOptions = func() {
globalPerTargetDialOptions = nil
}
internal.WithBinaryLogger = withBinaryLogger
internal.JoinDialOptions = newJoinDialOption
internal.DisableGlobalDialOptions = newDisableGlobalDialOptions
internal.WithBufferPool = withBufferPool
}
// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
unaryInt UnaryClientInterceptor
streamInt StreamClientInterceptor
chainUnaryInts []UnaryClientInterceptor
chainStreamInts []StreamClientInterceptor
compressorV0 Compressor
dc Decompressor
bs internalbackoff.Strategy
block bool
returnLastError bool
timeout time.Duration
authority string
binaryLogger binarylog.Logger
copts transport.ConnectOptions
callOptions []CallOption
channelzParent channelz.Identifier
disableServiceConfig bool
disableRetry bool
disableHealthCheck bool
minConnectTimeout func() time.Duration
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
defaultServiceConfigRawJSON *string
resolvers []resolver.Builder
idleTimeout time.Duration
defaultScheme string
maxCallAttempts int
enableLocalDNSResolution bool // Specifies if target hostnames should be resolved when proxying is enabled.
useProxy bool // Specifies if a server should be connected via proxy.
}
// DialOption configures how we set up the connection.
type DialOption interface {
apply(*dialOptions)
}
var globalDialOptions []DialOption
// perTargetDialOption takes a parsed target and returns a dial option to apply.
//
// This gets called after NewClient() parses the target, and allows per target
// configuration set through a returned DialOption. The DialOption will not take
// effect if specifies a resolver builder, as that Dial Option is factored in
// while parsing target.
type perTargetDialOption interface {
// DialOption returns a Dial Option to apply.
DialOptionForTarget(parsedTarget url.URL) DialOption
}
var globalPerTargetDialOptions []perTargetDialOption
// EmptyDialOption does not alter the dial configuration. It can be embedded in
// another structure to build custom dial options.
//
// # Experimental
//
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
// later release.
type EmptyDialOption struct{}
func (EmptyDialOption) apply(*dialOptions) {}
type disableGlobalDialOptions struct{}
func (disableGlobalDialOptions) apply(*dialOptions) {}
// newDisableGlobalDialOptions returns a DialOption that prevents the ClientConn
// from applying the global DialOptions (set via AddGlobalDialOptions).
func newDisableGlobalDialOptions() DialOption {
return &disableGlobalDialOptions{}
}
// funcDialOption wraps a function that modifies dialOptions into an
// implementation of the DialOption interface.
type funcDialOption struct {
f func(*dialOptions)
}
func (fdo *funcDialOption) apply(do *dialOptions) {
fdo.f(do)
}
func newFuncDialOption(f func(*dialOptions)) *funcDialOption {
return &funcDialOption{
f: f,
}
}
type joinDialOption struct {
opts []DialOption
}
func (jdo *joinDialOption) apply(do *dialOptions) {
for _, opt := range jdo.opts {
opt.apply(do)
}
}
func newJoinDialOption(opts ...DialOption) DialOption {
return &joinDialOption{opts: opts}
}
// WithSharedWriteBuffer allows reusing per-connection transport write buffer.
// If this option is set to true every connection will release the buffer after
// flushing the data on the wire.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithSharedWriteBuffer(val bool) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.SharedWriteBuffer = val
})
}
// WithWriteBufferSize determines how much data can be batched before doing a
// write on the wire. The default value for this buffer is 32KB.
//
// Zero or negative values will disable the write buffer such that each write
// will be on underlying connection. Note: A Send call may not directly
// translate to a write.
func WithWriteBufferSize(s int) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.WriteBufferSize = s
})
}
// WithReadBufferSize lets you set the size of read buffer, this determines how
// much data can be read at most for each read syscall.
//
// The default value for this buffer is 32KB. Zero or negative values will
// disable read buffer for a connection so data framer can access the
// underlying conn directly.
func WithReadBufferSize(s int) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.ReadBufferSize = s
})
}
// WithInitialWindowSize returns a DialOption which sets the value for initial
// window size on a stream. The lower bound for window size is 64K and any value
// smaller than that will be ignored.
func WithInitialWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialWindowSize = s
o.copts.StaticWindowSize = true
})
}
// WithInitialConnWindowSize returns a DialOption which sets the value for
// initial window size on a connection. The lower bound for window size is 64K
// and any value smaller than that will be ignored.
func WithInitialConnWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialConnWindowSize = s
o.copts.StaticWindowSize = true
})
}
// WithStaticStreamWindowSize returns a DialOption which sets the initial
// stream window size to the value provided and disables dynamic flow control.
func WithStaticStreamWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialWindowSize = s
o.copts.StaticWindowSize = true
})
}
// WithStaticConnWindowSize returns a DialOption which sets the initial
// connection window size to the value provided and disables dynamic flow
// control.
func WithStaticConnWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialConnWindowSize = s
o.copts.StaticWindowSize = true
})
}
// WithMaxMsgSize returns a DialOption which sets the maximum message size the
// client can receive.
//
// Deprecated: use WithDefaultCallOptions(MaxCallRecvMsgSize(s)) instead. Will
// be supported throughout 1.x.
func WithMaxMsgSize(s int) DialOption {
return WithDefaultCallOptions(MaxCallRecvMsgSize(s))
}
// WithDefaultCallOptions returns a DialOption which sets the default
// CallOptions for calls over the connection.
func WithDefaultCallOptions(cos ...CallOption) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.callOptions = append(o.callOptions, cos...)
})
}
// WithCodec returns a DialOption which sets a codec for message marshaling and
// unmarshaling.
//
// Deprecated: use WithDefaultCallOptions(ForceCodec(_)) instead. Will be
// supported throughout 1.x.
func WithCodec(c Codec) DialOption {
return WithDefaultCallOptions(CallCustomCodec(c))
}
// WithCompressor returns a DialOption which sets a Compressor to use for
// message compression. It has lower priority than the compressor set by the
// UseCompressor CallOption.
//
// Deprecated: use UseCompressor instead. Will be supported throughout 1.x.
func WithCompressor(cp Compressor) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.compressorV0 = cp
})
}
// WithDecompressor returns a DialOption which sets a Decompressor to use for
// incoming message decompression. If incoming response messages are encoded
// using the decompressor's Type(), it will be used. Otherwise, the message
// encoding will be used to look up the compressor registered via
// encoding.RegisterCompressor, which will then be used to decompress the
// message. If no compressor is registered for the encoding, an Unimplemented
// status error will be returned.
//
// Deprecated: use encoding.RegisterCompressor instead. Will be supported
// throughout 1.x.
func WithDecompressor(dc Decompressor) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.dc = dc
})
}
// WithConnectParams configures the ClientConn to use the provided ConnectParams
// for creating and maintaining connections to servers.
//
// The backoff configuration specified as part of the ConnectParams overrides
// all defaults specified in
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. Consider
// using the backoff.DefaultConfig as a base, in cases where you want to
// override only a subset of the backoff configuration.
func WithConnectParams(p ConnectParams) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.bs = internalbackoff.Exponential{Config: p.Backoff}
o.minConnectTimeout = func() time.Duration {
return p.MinConnectTimeout
}
})
}
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
// when backing off after failed connection attempts.
//
// Deprecated: use WithConnectParams instead. Will be supported throughout 1.x.
func WithBackoffMaxDelay(md time.Duration) DialOption {
return WithBackoffConfig(BackoffConfig{MaxDelay: md})
}
// WithBackoffConfig configures the dialer to use the provided backoff
// parameters after connection failures.
//
// Deprecated: use WithConnectParams instead. Will be supported throughout 1.x.
func WithBackoffConfig(b BackoffConfig) DialOption {
bc := backoff.DefaultConfig
bc.MaxDelay = b.MaxDelay
return withBackoff(internalbackoff.Exponential{Config: bc})
}
// withBackoff sets the backoff strategy used for connectRetryNum after a failed
// connection attempt.
//
// This can be exported if arbitrary backoff strategies are allowed by gRPC.
func withBackoff(bs internalbackoff.Strategy) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.bs = bs
})
}
// WithBlock returns a DialOption which makes callers of Dial block until the
// underlying connection is up. Without this, Dial returns immediately and
// connecting the server happens in background.
//
// Use of this feature is not recommended. For more information, please see:
// https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md
//
// Deprecated: this DialOption is not supported by NewClient.
// Will be supported throughout 1.x.
func WithBlock() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.block = true
})
}
// WithReturnConnectionError returns a DialOption which makes the client connection
// return a string containing both the last connection error that occurred and
// the context.DeadlineExceeded error.
// Implies WithBlock()
//
// Use of this feature is not recommended. For more information, please see:
// https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md
//
// Deprecated: this DialOption is not supported by NewClient.
// Will be supported throughout 1.x.
func WithReturnConnectionError() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.block = true
o.returnLastError = true
})
}
// WithInsecure returns a DialOption which disables transport security for this
// ClientConn. Under the hood, it uses insecure.NewCredentials().
//
// Note that using this DialOption with per-RPC credentials (through
// WithCredentialsBundle or WithPerRPCCredentials) which require transport
// security is incompatible and will cause RPCs to fail.
//
// Deprecated: use WithTransportCredentials and insecure.NewCredentials()
// instead. Will be supported throughout 1.x.
func WithInsecure() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.TransportCredentials = insecure.NewCredentials()
})
}
// WithNoProxy returns a DialOption which disables the use of proxies for this
// ClientConn. This is ignored if WithDialer or WithContextDialer are used.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithNoProxy() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.useProxy = false
})
}
// WithLocalDNSResolution forces local DNS name resolution even when a proxy is
// specified in the environment. By default, the server name is provided
// directly to the proxy as part of the CONNECT handshake. This is ignored if
// WithNoProxy is used.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithLocalDNSResolution() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.enableLocalDNSResolution = true
})
}
// WithTransportCredentials returns a DialOption which configures a connection
// level security credentials (e.g., TLS/SSL). This should not be used together
// with WithCredentialsBundle.
func WithTransportCredentials(creds credentials.TransportCredentials) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.TransportCredentials = creds
})
}
// WithPerRPCCredentials returns a DialOption which sets credentials and places
// auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.PerRPCCredentials = append(o.copts.PerRPCCredentials, creds)
})
}
// WithCredentialsBundle returns a DialOption to set a credentials bundle for
// the ClientConn.WithCreds. This should not be used together with
// WithTransportCredentials.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithCredentialsBundle(b credentials.Bundle) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.CredsBundle = b
})
}
// WithTimeout returns a DialOption that configures a timeout for dialing a
// ClientConn initially. This is valid if and only if WithBlock() is present.
//
// Deprecated: this DialOption is not supported by NewClient.
// Will be supported throughout 1.x.
func WithTimeout(d time.Duration) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.timeout = d
})
}
// WithContextDialer returns a DialOption that sets a dialer to create
// connections. If FailOnNonTempDialError() is set to true, and an error is
// returned by f, gRPC checks the error's Temporary() method to decide if it
// should try to reconnect to the network address.
//
// Note that gRPC by default performs name resolution on the target passed to
// NewClient. To bypass name resolution and cause the target string to be
// passed directly to the dialer here instead, use the "passthrough" resolver
// by specifying it in the target string, e.g. "passthrough:target".
//
// Note: All supported releases of Go (as of December 2023) override the OS
// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive
// with OS defaults for keepalive time and interval, use a net.Dialer that sets
// the KeepAlive field to a negative value, and sets the SO_KEEPALIVE socket
// option to true from the Control field. For a concrete example of how to do
// this, see internal.NetDialerWithTCPKeepalive().
//
// For more information, please see [issue 23459] in the Go GitHub repo.
//
// [issue 23459]: https://github.com/golang/go/issues/23459
func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.Dialer = f
})
}
// WithDialer returns a DialOption that specifies a function to use for dialing
// network addresses. If FailOnNonTempDialError() is set to true, and an error
// is returned by f, gRPC checks the error's Temporary() method to decide if it
// should try to reconnect to the network address.
//
// Deprecated: use WithContextDialer instead. Will be supported throughout
// 1.x.
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
return WithContextDialer(
func(ctx context.Context, addr string) (net.Conn, error) {
if deadline, ok := ctx.Deadline(); ok {
return f(addr, time.Until(deadline))
}
return f(addr, 0)
})
}
// WithStatsHandler returns a DialOption that specifies the stats handler for
// all the RPCs and underlying network connections in this ClientConn.
func WithStatsHandler(h stats.Handler) DialOption {
return newFuncDialOption(func(o *dialOptions) {
if h == nil {
logger.Error("ignoring nil parameter in grpc.WithStatsHandler ClientOption")
// Do not allow a nil stats handler, which would otherwise cause
// panics.
return
}
o.copts.StatsHandlers = append(o.copts.StatsHandlers, h)
})
}
// withBinaryLogger returns a DialOption that specifies the binary logger for
// this ClientConn.
func withBinaryLogger(bl binarylog.Logger) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.binaryLogger = bl
})
}
// FailOnNonTempDialError returns a DialOption that specifies if gRPC fails on
// non-temporary dial errors. If f is true, and dialer returns a non-temporary
// error, gRPC will fail the connection to the network address and won't try to
// reconnect. The default value of FailOnNonTempDialError is false.
//
// FailOnNonTempDialError only affects the initial dial, and does not do
// anything useful unless you are also using WithBlock().
//
// Use of this feature is not recommended. For more information, please see:
// https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md
//
// Deprecated: this DialOption is not supported by NewClient.
// This API may be changed or removed in a
// later release.
func FailOnNonTempDialError(f bool) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.FailOnNonTempDialError = f
})
}
// WithUserAgent returns a DialOption that specifies a user agent string for all
// the RPCs.
func WithUserAgent(s string) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.UserAgent = s + " " + grpcUA
})
}
// WithKeepaliveParams returns a DialOption that specifies keepalive parameters
// for the client transport.
//
// Keepalive is disabled by default.
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
if kp.Time < internal.KeepaliveMinPingTime {
logger.Warningf("Adjusting keepalive ping interval to minimum period of %v", internal.KeepaliveMinPingTime)
kp.Time = internal.KeepaliveMinPingTime
}
return newFuncDialOption(func(o *dialOptions) {
o.copts.KeepaliveParams = kp
})
}
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for
// unary RPCs.
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.unaryInt = f
})
}
// WithChainUnaryInterceptor returns a DialOption that specifies the chained
// interceptor for unary RPCs. The first interceptor will be the outer most,
// while the last interceptor will be the inner most wrapper around the real call.
// All interceptors added by this method will be chained, and the interceptor
// defined by WithUnaryInterceptor will always be prepended to the chain.
func WithChainUnaryInterceptor(interceptors ...UnaryClientInterceptor) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.chainUnaryInts = append(o.chainUnaryInts, interceptors...)
})
}
// WithStreamInterceptor returns a DialOption that specifies the interceptor for
// streaming RPCs.
func WithStreamInterceptor(f StreamClientInterceptor) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.streamInt = f
})
}
// WithChainStreamInterceptor returns a DialOption that specifies the chained
// interceptor for streaming RPCs. The first interceptor will be the outer most,
// while the last interceptor will be the inner most wrapper around the real call.
// All interceptors added by this method will be chained, and the interceptor
// defined by WithStreamInterceptor will always be prepended to the chain.
func WithChainStreamInterceptor(interceptors ...StreamClientInterceptor) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.chainStreamInts = append(o.chainStreamInts, interceptors...)
})
}
// WithAuthority returns a DialOption that specifies the value to be used as the
// :authority pseudo-header and as the server name in authentication handshake.
// This overrides all other ways of setting authority on the channel, but can be
// overridden per-call by using grpc.CallAuthority.
func WithAuthority(a string) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.authority = a
})
}
// WithChannelzParentID returns a DialOption that specifies the channelz ID of
// current ClientConn's parent. This function is used in nested channel creation
// (e.g. grpclb dial).
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithChannelzParentID(c channelz.Identifier) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.channelzParent = c
})
}
// WithDisableServiceConfig returns a DialOption that causes gRPC to ignore any
// service config provided by the resolver and provides a hint to the resolver
// to not fetch service configs.
//
// Note that this dial option only disables service config from resolver. If
// default service config is provided, gRPC will use the default service config.
func WithDisableServiceConfig() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.disableServiceConfig = true
})
}
// WithDefaultServiceConfig returns a DialOption that configures the default
// service config, which will be used in cases where:
//
// 1. WithDisableServiceConfig is also used, or
//
// 2. The name resolver does not provide a service config or provides an
// invalid service config.
//
// The parameter s is the JSON representation of the default service config.
// For more information about service configs, see:
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
// For a simple example of usage, see:
// examples/features/load_balancing/client/main.go
func WithDefaultServiceConfig(s string) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.defaultServiceConfigRawJSON = &s
})
}
// WithDisableRetry returns a DialOption that disables retries, even if the
// service config enables them. This does not impact transparent retries, which
// will happen automatically if no data is written to the wire or if the RPC is
// unprocessed by the remote server.
func WithDisableRetry() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.disableRetry = true
})
}
// MaxHeaderListSizeDialOption is a DialOption that specifies the maximum
// (uncompressed) size of header list that the client is prepared to accept.
type MaxHeaderListSizeDialOption struct {
MaxHeaderListSize uint32
}
func (o MaxHeaderListSizeDialOption) apply(do *dialOptions) {
do.copts.MaxHeaderListSize = &o.MaxHeaderListSize
}
// WithMaxHeaderListSize returns a DialOption that specifies the maximum
// (uncompressed) size of header list that the client is prepared to accept.
func WithMaxHeaderListSize(s uint32) DialOption {
return MaxHeaderListSizeDialOption{
MaxHeaderListSize: s,
}
}
// WithDisableHealthCheck disables the LB channel health checking for all
// SubConns of this ClientConn.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithDisableHealthCheck() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.disableHealthCheck = true
})
}
func defaultDialOptions() dialOptions {
return dialOptions{
copts: transport.ConnectOptions{
ReadBufferSize: defaultReadBufSize,
WriteBufferSize: defaultWriteBufSize,
UserAgent: grpcUA,
BufferPool: mem.DefaultBufferPool(),
},
bs: internalbackoff.DefaultExponential,
idleTimeout: 30 * time.Minute,
defaultScheme: "dns",
maxCallAttempts: defaultMaxCallAttempts,
useProxy: true,
enableLocalDNSResolution: false,
}
}
// withMinConnectDeadline specifies the function that clientconn uses to
// get minConnectDeadline. This can be used to make connection attempts happen
// faster/slower.
//
// For testing purpose only.
func withMinConnectDeadline(f func() time.Duration) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.minConnectTimeout = f
})
}
// withDefaultScheme is used to allow Dial to use "passthrough" as the default
// name resolver, while NewClient uses "dns" otherwise.
func withDefaultScheme(s string) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.defaultScheme = s
})
}
// WithResolvers allows a list of resolver implementations to be registered
// locally with the ClientConn without needing to be globally registered via
// resolver.Register. They will be matched against the scheme used for the
// current Dial only, and will take precedence over the global registry.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithResolvers(rs ...resolver.Builder) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.resolvers = append(o.resolvers, rs...)
})
}
// WithIdleTimeout returns a DialOption that configures an idle timeout for the
// channel. If the channel is idle for the configured timeout, i.e there are no
// ongoing RPCs and no new RPCs are initiated, the channel will enter idle mode
// and as a result the name resolver and load balancer will be shut down. The
// channel will exit idle mode when the Connect() method is called or when an
// RPC is initiated.
//
// A default timeout of 30 minutes will be used if this dial option is not set
// at dial time and idleness can be disabled by passing a timeout of zero.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithIdleTimeout(d time.Duration) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.idleTimeout = d
})
}
// WithMaxCallAttempts returns a DialOption that configures the maximum number
// of attempts per call (including retries and hedging) using the channel.
// Service owners may specify a higher value for these parameters, but higher
// values will be treated as equal to the maximum value by the client
// implementation. This mitigates security concerns related to the service
// config being transferred to the client via DNS.
//
// A value of 5 will be used if this dial option is not set or n < 2.
func WithMaxCallAttempts(n int) DialOption {
return newFuncDialOption(func(o *dialOptions) {
if n < 2 {
n = defaultMaxCallAttempts
}
o.maxCallAttempts = n
})
}
func withBufferPool(bufferPool mem.BufferPool) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.BufferPool = bufferPool
})
}

26
vendor/google.golang.org/grpc/doc.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
//go:generate ./scripts/regenerate.sh
/*
Package grpc implements an RPC system called gRPC.
See grpc.io for more information about gRPC.
*/
package grpc // import "google.golang.org/grpc"

151
vendor/google.golang.org/grpc/encoding/encoding.go generated vendored Normal file
View File

@@ -0,0 +1,151 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package encoding defines the interface for the compressor and codec, and
// functions to register and retrieve compressors and codecs.
//
// # Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
// later release.
package encoding
import (
"io"
"slices"
"strings"
"google.golang.org/grpc/encoding/internal"
"google.golang.org/grpc/internal/grpcutil"
)
// Identity specifies the optional encoding for uncompressed streams.
// It is intended for grpc internal use only.
const Identity = "identity"
func init() {
internal.RegisterCompressorForTesting = func(c Compressor) func() {
name := c.Name()
curCompressor, found := registeredCompressor[name]
RegisterCompressor(c)
return func() {
if found {
registeredCompressor[name] = curCompressor
return
}
delete(registeredCompressor, name)
grpcutil.RegisteredCompressorNames = slices.DeleteFunc(grpcutil.RegisteredCompressorNames, func(s string) bool {
return s == name
})
}
}
}
// Compressor is used for compressing and decompressing when sending or
// receiving messages.
//
// If a Compressor implements `DecompressedSize(compressedBytes []byte) int`,
// gRPC will invoke it to determine the size of the buffer allocated for the
// result of decompression. A return value of -1 indicates unknown size.
type Compressor interface {
// Compress writes the data written to wc to w after compressing it. If an
// error occurs while initializing the compressor, that error is returned
// instead.
Compress(w io.Writer) (io.WriteCloser, error)
// Decompress reads data from r, decompresses it, and provides the
// uncompressed data via the returned io.Reader. If an error occurs while
// initializing the decompressor, that error is returned instead.
Decompress(r io.Reader) (io.Reader, error)
// Name is the name of the compression codec and is used to set the content
// coding header. The result must be static; the result cannot change
// between calls.
Name() string
}
var registeredCompressor = make(map[string]Compressor)
// RegisterCompressor registers the compressor with gRPC by its name. It can
// be activated when sending an RPC via grpc.UseCompressor(). It will be
// automatically accessed when receiving a message based on the content coding
// header. Servers also use it to send a response with the same encoding as
// the request.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple Compressors are
// registered with the same name, the one registered last will take effect.
func RegisterCompressor(c Compressor) {
registeredCompressor[c.Name()] = c
if !grpcutil.IsCompressorNameRegistered(c.Name()) {
grpcutil.RegisteredCompressorNames = append(grpcutil.RegisteredCompressorNames, c.Name())
}
}
// GetCompressor returns Compressor for the given compressor name.
func GetCompressor(name string) Compressor {
return registeredCompressor[name]
}
// Codec defines the interface gRPC uses to encode and decode messages. Note
// that implementations of this interface must be thread safe; a Codec's
// methods can be called from concurrent goroutines.
type Codec interface {
// Marshal returns the wire format of v.
Marshal(v any) ([]byte, error)
// Unmarshal parses the wire format into v.
Unmarshal(data []byte, v any) error
// Name returns the name of the Codec implementation. The returned string
// will be used as part of content type in transmission. The result must be
// static; the result cannot change between calls.
Name() string
}
var registeredCodecs = make(map[string]any)
// RegisterCodec registers the provided Codec for use with all gRPC clients and
// servers.
//
// The Codec will be stored and looked up by result of its Name() method, which
// should match the content-subtype of the encoding handled by the Codec. This
// is case-insensitive, and is stored and looked up as lowercase. If the
// result of calling Name() is an empty string, RegisterCodec will panic. See
// Content-Type on
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
// more details.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple Codecs are
// registered with the same name, the one registered last will take effect.
func RegisterCodec(codec Codec) {
if codec == nil {
panic("cannot register a nil Codec")
}
if codec.Name() == "" {
panic("cannot register Codec with empty string result for Name()")
}
contentSubtype := strings.ToLower(codec.Name())
registeredCodecs[contentSubtype] = codec
}
// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is
// registered for the content-subtype.
//
// The content-subtype is expected to be lowercase.
func GetCodec(contentSubtype string) Codec {
c, _ := registeredCodecs[contentSubtype].(Codec)
return c
}

81
vendor/google.golang.org/grpc/encoding/encoding_v2.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package encoding
import (
"strings"
"google.golang.org/grpc/mem"
)
// CodecV2 defines the interface gRPC uses to encode and decode messages. Note
// that implementations of this interface must be thread safe; a CodecV2's
// methods can be called from concurrent goroutines.
type CodecV2 interface {
// Marshal returns the wire format of v. The buffers in the returned
// [mem.BufferSlice] must have at least one reference each, which will be freed
// by gRPC when they are no longer needed.
Marshal(v any) (out mem.BufferSlice, err error)
// Unmarshal parses the wire format into v. Note that data will be freed as soon
// as this function returns. If the codec wishes to guarantee access to the data
// after this function, it must take its own reference that it frees when it is
// no longer needed.
Unmarshal(data mem.BufferSlice, v any) error
// Name returns the name of the Codec implementation. The returned string
// will be used as part of content type in transmission. The result must be
// static; the result cannot change between calls.
Name() string
}
// RegisterCodecV2 registers the provided CodecV2 for use with all gRPC clients and
// servers.
//
// The CodecV2 will be stored and looked up by result of its Name() method, which
// should match the content-subtype of the encoding handled by the CodecV2. This
// is case-insensitive, and is stored and looked up as lowercase. If the
// result of calling Name() is an empty string, RegisterCodecV2 will panic. See
// Content-Type on
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
// more details.
//
// If both a Codec and CodecV2 are registered with the same name, the CodecV2
// will be used.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple Codecs are
// registered with the same name, the one registered last will take effect.
func RegisterCodecV2(codec CodecV2) {
if codec == nil {
panic("cannot register a nil CodecV2")
}
if codec.Name() == "" {
panic("cannot register CodecV2 with empty string result for Name()")
}
contentSubtype := strings.ToLower(codec.Name())
registeredCodecs[contentSubtype] = codec
}
// GetCodecV2 gets a registered CodecV2 by content-subtype, or nil if no CodecV2 is
// registered for the content-subtype.
//
// The content-subtype is expected to be lowercase.
func GetCodecV2(contentSubtype string) CodecV2 {
c, _ := registeredCodecs[contentSubtype].(CodecV2)
return c
}

View File

@@ -0,0 +1,28 @@
/*
*
* Copyright 2025 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package internal contains code internal to the encoding package.
package internal
// RegisterCompressorForTesting registers a compressor in the global compressor
// registry. It returns a cleanup function that should be called at the end
// of the test to unregister the compressor.
//
// This prevents compressors registered in one test from appearing in the
// encoding headers of subsequent tests.
var RegisterCompressorForTesting any // func RegisterCompressor(c Compressor) func()

112
vendor/google.golang.org/grpc/encoding/proto/proto.go generated vendored Normal file
View File

@@ -0,0 +1,112 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package proto defines the protobuf codec. Importing this package will
// register the codec.
package proto
import (
"fmt"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/mem"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/protoadapt"
)
// Name is the name registered for the proto compressor.
const Name = "proto"
func init() {
encoding.RegisterCodecV2(&codecV2{})
}
// codec is a CodecV2 implementation with protobuf. It is the default codec for
// gRPC.
type codecV2 struct{}
func (c *codecV2) Marshal(v any) (data mem.BufferSlice, err error) {
vv := messageV2Of(v)
if vv == nil {
return nil, fmt.Errorf("proto: failed to marshal, message is %T, want proto.Message", v)
}
// Important: if we remove this Size call then we cannot use
// UseCachedSize in MarshalOptions below.
size := proto.Size(vv)
// MarshalOptions with UseCachedSize allows reusing the result from the
// previous Size call. This is safe here because:
//
// 1. We just computed the size.
// 2. We assume the message is not being mutated concurrently.
//
// Important: If the proto.Size call above is removed, using UseCachedSize
// becomes unsafe and may lead to incorrect marshaling.
//
// For more details, see the doc of UseCachedSize:
// https://pkg.go.dev/google.golang.org/protobuf/proto#MarshalOptions
marshalOptions := proto.MarshalOptions{UseCachedSize: true}
if mem.IsBelowBufferPoolingThreshold(size) {
buf, err := marshalOptions.Marshal(vv)
if err != nil {
return nil, err
}
data = append(data, mem.SliceBuffer(buf))
} else {
pool := mem.DefaultBufferPool()
buf := pool.Get(size)
if _, err := marshalOptions.MarshalAppend((*buf)[:0], vv); err != nil {
pool.Put(buf)
return nil, err
}
data = append(data, mem.NewBuffer(buf, pool))
}
return data, nil
}
func (c *codecV2) Unmarshal(data mem.BufferSlice, v any) (err error) {
vv := messageV2Of(v)
if vv == nil {
return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v)
}
buf := data.MaterializeToBuffer(mem.DefaultBufferPool())
defer buf.Free()
// TODO: Upgrade proto.Unmarshal to support mem.BufferSlice. Right now, it's not
// really possible without a major overhaul of the proto package, but the
// vtprotobuf library may be able to support this.
return proto.Unmarshal(buf.ReadOnlyData(), vv)
}
func messageV2Of(v any) proto.Message {
switch v := v.(type) {
case protoadapt.MessageV1:
return protoadapt.MessageV2Of(v)
case protoadapt.MessageV2:
return v
}
return nil
}
func (c *codecV2) Name() string {
return Name
}

View File

@@ -0,0 +1,342 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package stats
import (
"maps"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/stats"
)
func init() {
internal.SnapshotMetricRegistryForTesting = snapshotMetricsRegistryForTesting
}
var logger = grpclog.Component("metrics-registry")
// DefaultMetrics are the default metrics registered through global metrics
// registry. This is written to at initialization time only, and is read only
// after initialization.
var DefaultMetrics = stats.NewMetricSet()
// MetricDescriptor is the data for a registered metric.
type MetricDescriptor struct {
// The name of this metric. This name must be unique across the whole binary
// (including any per call metrics). See
// https://github.com/grpc/proposal/blob/master/A79-non-per-call-metrics-architecture.md#metric-instrument-naming-conventions
// for metric naming conventions.
Name string
// The description of this metric.
Description string
// The unit (e.g. entries, seconds) of this metric.
Unit string
// The required label keys for this metric. These are intended to
// metrics emitted from a stats handler.
Labels []string
// The optional label keys for this metric. These are intended to attached
// to metrics emitted from a stats handler if configured.
OptionalLabels []string
// Whether this metric is on by default.
Default bool
// The type of metric. This is set by the metric registry, and not intended
// to be set by a component registering a metric.
Type MetricType
// Bounds are the bounds of this metric. This only applies to histogram
// metrics. If unset or set with length 0, stats handlers will fall back to
// default bounds.
Bounds []float64
}
// MetricType is the type of metric.
type MetricType int
// Type of metric supported by this instrument registry.
const (
MetricTypeIntCount MetricType = iota
MetricTypeFloatCount
MetricTypeIntHisto
MetricTypeFloatHisto
MetricTypeIntGauge
MetricTypeIntUpDownCount
MetricTypeIntAsyncGauge
)
// Int64CountHandle is a typed handle for a int count metric. This handle
// is passed at the recording point in order to know which metric to record
// on.
type Int64CountHandle MetricDescriptor
// Descriptor returns the int64 count handle typecast to a pointer to a
// MetricDescriptor.
func (h *Int64CountHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the int64 count value on the metrics recorder provided.
func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
recorder.RecordInt64Count(h, incr, labels...)
}
// Int64UpDownCountHandle is a typed handle for an int up-down counter metric.
// This handle is passed at the recording point in order to know which metric
// to record on.
type Int64UpDownCountHandle MetricDescriptor
// Descriptor returns the int64 up-down counter handle typecast to a pointer to a
// MetricDescriptor.
func (h *Int64UpDownCountHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the int64 up-down counter value on the metrics recorder provided.
// The value 'v' can be positive to increment or negative to decrement.
func (h *Int64UpDownCountHandle) Record(recorder MetricsRecorder, v int64, labels ...string) {
recorder.RecordInt64UpDownCount(h, v, labels...)
}
// Float64CountHandle is a typed handle for a float count metric. This handle is
// passed at the recording point in order to know which metric to record on.
type Float64CountHandle MetricDescriptor
// Descriptor returns the float64 count handle typecast to a pointer to a
// MetricDescriptor.
func (h *Float64CountHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the float64 count value on the metrics recorder provided.
func (h *Float64CountHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
recorder.RecordFloat64Count(h, incr, labels...)
}
// Int64HistoHandle is a typed handle for an int histogram metric. This handle
// is passed at the recording point in order to know which metric to record on.
type Int64HistoHandle MetricDescriptor
// Descriptor returns the int64 histo handle typecast to a pointer to a
// MetricDescriptor.
func (h *Int64HistoHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the int64 histo value on the metrics recorder provided.
func (h *Int64HistoHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
recorder.RecordInt64Histo(h, incr, labels...)
}
// Float64HistoHandle is a typed handle for a float histogram metric. This
// handle is passed at the recording point in order to know which metric to
// record on.
type Float64HistoHandle MetricDescriptor
// Descriptor returns the float64 histo handle typecast to a pointer to a
// MetricDescriptor.
func (h *Float64HistoHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the float64 histo value on the metrics recorder provided.
func (h *Float64HistoHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
recorder.RecordFloat64Histo(h, incr, labels...)
}
// Int64GaugeHandle is a typed handle for an int gauge metric. This handle is
// passed at the recording point in order to know which metric to record on.
type Int64GaugeHandle MetricDescriptor
// Descriptor returns the int64 gauge handle typecast to a pointer to a
// MetricDescriptor.
func (h *Int64GaugeHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the int64 histo value on the metrics recorder provided.
func (h *Int64GaugeHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
recorder.RecordInt64Gauge(h, incr, labels...)
}
// AsyncMetric is a marker interface for asynchronous metric types.
type AsyncMetric interface {
isAsync()
Descriptor() *MetricDescriptor
}
// Int64AsyncGaugeHandle is a typed handle for an int gauge metric. This handle is
// passed at the recording point in order to know which metric to record on.
type Int64AsyncGaugeHandle MetricDescriptor
// isAsync implements the AsyncMetric interface.
func (h *Int64AsyncGaugeHandle) isAsync() {}
// Descriptor returns the int64 gauge handle typecast to a pointer to a
// MetricDescriptor.
func (h *Int64AsyncGaugeHandle) Descriptor() *MetricDescriptor {
return (*MetricDescriptor)(h)
}
// Record records the int64 gauge value on the metrics recorder provided.
func (h *Int64AsyncGaugeHandle) Record(recorder AsyncMetricsRecorder, value int64, labels ...string) {
recorder.RecordInt64AsyncGauge(h, value, labels...)
}
// registeredMetrics are the registered metric descriptor names.
var registeredMetrics = make(map[string]bool)
// metricsRegistry contains all of the registered metrics.
//
// This is written to only at init time, and read only after that.
var metricsRegistry = make(map[string]*MetricDescriptor)
// DescriptorForMetric returns the MetricDescriptor from the global registry.
//
// Returns nil if MetricDescriptor not present.
func DescriptorForMetric(metricName string) *MetricDescriptor {
return metricsRegistry[metricName]
}
func registerMetric(metricName string, def bool) {
if registeredMetrics[metricName] {
logger.Fatalf("metric %v already registered", metricName)
}
registeredMetrics[metricName] = true
if def {
DefaultMetrics = DefaultMetrics.Add(metricName)
}
}
// RegisterInt64Count registers the metric description onto the global registry.
// It returns a typed handle to use to recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterInt64Count(descriptor MetricDescriptor) *Int64CountHandle {
registerMetric(descriptor.Name, descriptor.Default)
descriptor.Type = MetricTypeIntCount
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Int64CountHandle)(descPtr)
}
// RegisterFloat64Count registers the metric description onto the global
// registry. It returns a typed handle to use to recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterFloat64Count(descriptor MetricDescriptor) *Float64CountHandle {
registerMetric(descriptor.Name, descriptor.Default)
descriptor.Type = MetricTypeFloatCount
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Float64CountHandle)(descPtr)
}
// RegisterInt64Histo registers the metric description onto the global registry.
// It returns a typed handle to use to recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterInt64Histo(descriptor MetricDescriptor) *Int64HistoHandle {
registerMetric(descriptor.Name, descriptor.Default)
descriptor.Type = MetricTypeIntHisto
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Int64HistoHandle)(descPtr)
}
// RegisterFloat64Histo registers the metric description onto the global
// registry. It returns a typed handle to use to recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterFloat64Histo(descriptor MetricDescriptor) *Float64HistoHandle {
registerMetric(descriptor.Name, descriptor.Default)
descriptor.Type = MetricTypeFloatHisto
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Float64HistoHandle)(descPtr)
}
// RegisterInt64Gauge registers the metric description onto the global registry.
// It returns a typed handle to use to recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterInt64Gauge(descriptor MetricDescriptor) *Int64GaugeHandle {
registerMetric(descriptor.Name, descriptor.Default)
descriptor.Type = MetricTypeIntGauge
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Int64GaugeHandle)(descPtr)
}
// RegisterInt64UpDownCount registers the metric description onto the global registry.
// It returns a typed handle to use for recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterInt64UpDownCount(descriptor MetricDescriptor) *Int64UpDownCountHandle {
registerMetric(descriptor.Name, descriptor.Default)
// Set the specific metric type for the up-down counter
descriptor.Type = MetricTypeIntUpDownCount
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Int64UpDownCountHandle)(descPtr)
}
// RegisterInt64AsyncGauge registers the metric description onto the global registry.
// It returns a typed handle to use for recording data.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple metrics are
// registered with the same name, this function will panic.
func RegisterInt64AsyncGauge(descriptor MetricDescriptor) *Int64AsyncGaugeHandle {
registerMetric(descriptor.Name, descriptor.Default)
descriptor.Type = MetricTypeIntAsyncGauge
descPtr := &descriptor
metricsRegistry[descriptor.Name] = descPtr
return (*Int64AsyncGaugeHandle)(descPtr)
}
// snapshotMetricsRegistryForTesting snapshots the global data of the metrics
// registry. Returns a cleanup function that sets the metrics registry to its
// original state.
func snapshotMetricsRegistryForTesting() func() {
oldDefaultMetrics := DefaultMetrics
oldRegisteredMetrics := registeredMetrics
oldMetricsRegistry := metricsRegistry
registeredMetrics = make(map[string]bool)
metricsRegistry = make(map[string]*MetricDescriptor)
maps.Copy(registeredMetrics, registeredMetrics)
maps.Copy(metricsRegistry, metricsRegistry)
return func() {
DefaultMetrics = oldDefaultMetrics
registeredMetrics = oldRegisteredMetrics
metricsRegistry = oldMetricsRegistry
}
}

View File

@@ -0,0 +1,64 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package stats contains experimental metrics/stats API's.
package stats
import "google.golang.org/grpc/stats"
// MetricsRecorder records on metrics derived from metric registry.
type MetricsRecorder interface {
// RecordInt64Count records the measurement alongside labels on the int
// count associated with the provided handle.
RecordInt64Count(handle *Int64CountHandle, incr int64, labels ...string)
// RecordFloat64Count records the measurement alongside labels on the float
// count associated with the provided handle.
RecordFloat64Count(handle *Float64CountHandle, incr float64, labels ...string)
// RecordInt64Histo records the measurement alongside labels on the int
// histo associated with the provided handle.
RecordInt64Histo(handle *Int64HistoHandle, incr int64, labels ...string)
// RecordFloat64Histo records the measurement alongside labels on the float
// histo associated with the provided handle.
RecordFloat64Histo(handle *Float64HistoHandle, incr float64, labels ...string)
// RecordInt64Gauge records the measurement alongside labels on the int
// gauge associated with the provided handle.
RecordInt64Gauge(handle *Int64GaugeHandle, incr int64, labels ...string)
// RecordInt64UpDownCounter records the measurement alongside labels on the int
// count associated with the provided handle.
RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string)
}
// AsyncMetricsRecorder records on asynchronous metrics derived from metric registry.
type AsyncMetricsRecorder interface {
// RecordInt64AsyncGauge records the measurement alongside labels on the int
// count associated with the provided handle asynchronously
RecordInt64AsyncGauge(handle *Int64AsyncGaugeHandle, incr int64, labels ...string)
}
// Metrics is an experimental legacy alias of the now-stable stats.MetricSet.
// Metrics will be deleted in a future release.
type Metrics = stats.MetricSet
// Metric was replaced by direct usage of strings.
type Metric = string
// NewMetrics is an experimental legacy alias of the now-stable
// stats.NewMetricSet. NewMetrics will be deleted in a future release.
func NewMetrics(metrics ...Metric) *Metrics {
return stats.NewMetricSet(metrics...)
}

115
vendor/google.golang.org/grpc/grpclog/component.go generated vendored Normal file
View File

@@ -0,0 +1,115 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpclog
import (
"fmt"
)
// componentData records the settings for a component.
type componentData struct {
name string
}
var cache = map[string]*componentData{}
func (c *componentData) InfoDepth(depth int, args ...any) {
args = append([]any{"[" + string(c.name) + "]"}, args...)
InfoDepth(depth+1, args...)
}
func (c *componentData) WarningDepth(depth int, args ...any) {
args = append([]any{"[" + string(c.name) + "]"}, args...)
WarningDepth(depth+1, args...)
}
func (c *componentData) ErrorDepth(depth int, args ...any) {
args = append([]any{"[" + string(c.name) + "]"}, args...)
ErrorDepth(depth+1, args...)
}
func (c *componentData) FatalDepth(depth int, args ...any) {
args = append([]any{"[" + string(c.name) + "]"}, args...)
FatalDepth(depth+1, args...)
}
func (c *componentData) Info(args ...any) {
c.InfoDepth(1, args...)
}
func (c *componentData) Warning(args ...any) {
c.WarningDepth(1, args...)
}
func (c *componentData) Error(args ...any) {
c.ErrorDepth(1, args...)
}
func (c *componentData) Fatal(args ...any) {
c.FatalDepth(1, args...)
}
func (c *componentData) Infof(format string, args ...any) {
c.InfoDepth(1, fmt.Sprintf(format, args...))
}
func (c *componentData) Warningf(format string, args ...any) {
c.WarningDepth(1, fmt.Sprintf(format, args...))
}
func (c *componentData) Errorf(format string, args ...any) {
c.ErrorDepth(1, fmt.Sprintf(format, args...))
}
func (c *componentData) Fatalf(format string, args ...any) {
c.FatalDepth(1, fmt.Sprintf(format, args...))
}
func (c *componentData) Infoln(args ...any) {
c.InfoDepth(1, args...)
}
func (c *componentData) Warningln(args ...any) {
c.WarningDepth(1, args...)
}
func (c *componentData) Errorln(args ...any) {
c.ErrorDepth(1, args...)
}
func (c *componentData) Fatalln(args ...any) {
c.FatalDepth(1, args...)
}
func (c *componentData) V(l int) bool {
return V(l)
}
// Component creates a new component and returns it for logging. If a component
// with the name already exists, nothing will be created and it will be
// returned. SetLoggerV2 will panic if it is called with a logger created by
// Component.
func Component(componentName string) DepthLoggerV2 {
if cData, ok := cache[componentName]; ok {
return cData
}
c := &componentData{componentName}
cache[componentName] = c
return c
}

186
vendor/google.golang.org/grpc/grpclog/grpclog.go generated vendored Normal file
View File

@@ -0,0 +1,186 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package grpclog defines logging for grpc.
//
// In the default logger, severity level can be set by environment variable
// GRPC_GO_LOG_SEVERITY_LEVEL, verbosity level can be set by
// GRPC_GO_LOG_VERBOSITY_LEVEL.
package grpclog
import (
"os"
"google.golang.org/grpc/grpclog/internal"
)
func init() {
SetLoggerV2(newLoggerV2())
}
// V reports whether verbosity level l is at least the requested verbose level.
func V(l int) bool {
return internal.LoggerV2Impl.V(l)
}
// Info logs to the INFO log.
func Info(args ...any) {
internal.LoggerV2Impl.Info(args...)
}
// Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf.
func Infof(format string, args ...any) {
internal.LoggerV2Impl.Infof(format, args...)
}
// Infoln logs to the INFO log. Arguments are handled in the manner of fmt.Println.
func Infoln(args ...any) {
internal.LoggerV2Impl.Infoln(args...)
}
// Warning logs to the WARNING log.
func Warning(args ...any) {
internal.LoggerV2Impl.Warning(args...)
}
// Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf.
func Warningf(format string, args ...any) {
internal.LoggerV2Impl.Warningf(format, args...)
}
// Warningln logs to the WARNING log. Arguments are handled in the manner of fmt.Println.
func Warningln(args ...any) {
internal.LoggerV2Impl.Warningln(args...)
}
// Error logs to the ERROR log.
func Error(args ...any) {
internal.LoggerV2Impl.Error(args...)
}
// Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf.
func Errorf(format string, args ...any) {
internal.LoggerV2Impl.Errorf(format, args...)
}
// Errorln logs to the ERROR log. Arguments are handled in the manner of fmt.Println.
func Errorln(args ...any) {
internal.LoggerV2Impl.Errorln(args...)
}
// Fatal logs to the FATAL log. Arguments are handled in the manner of fmt.Print.
// It calls os.Exit() with exit code 1.
func Fatal(args ...any) {
internal.LoggerV2Impl.Fatal(args...)
// Make sure fatal logs will exit.
os.Exit(1)
}
// Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf.
// It calls os.Exit() with exit code 1.
func Fatalf(format string, args ...any) {
internal.LoggerV2Impl.Fatalf(format, args...)
// Make sure fatal logs will exit.
os.Exit(1)
}
// Fatalln logs to the FATAL log. Arguments are handled in the manner of fmt.Println.
// It calls os.Exit() with exit code 1.
func Fatalln(args ...any) {
internal.LoggerV2Impl.Fatalln(args...)
// Make sure fatal logs will exit.
os.Exit(1)
}
// Print prints to the logger. Arguments are handled in the manner of fmt.Print.
//
// Deprecated: use Info.
func Print(args ...any) {
internal.LoggerV2Impl.Info(args...)
}
// Printf prints to the logger. Arguments are handled in the manner of fmt.Printf.
//
// Deprecated: use Infof.
func Printf(format string, args ...any) {
internal.LoggerV2Impl.Infof(format, args...)
}
// Println prints to the logger. Arguments are handled in the manner of fmt.Println.
//
// Deprecated: use Infoln.
func Println(args ...any) {
internal.LoggerV2Impl.Infoln(args...)
}
// InfoDepth logs to the INFO log at the specified depth.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func InfoDepth(depth int, args ...any) {
if internal.DepthLoggerV2Impl != nil {
internal.DepthLoggerV2Impl.InfoDepth(depth, args...)
} else {
internal.LoggerV2Impl.Infoln(args...)
}
}
// WarningDepth logs to the WARNING log at the specified depth.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WarningDepth(depth int, args ...any) {
if internal.DepthLoggerV2Impl != nil {
internal.DepthLoggerV2Impl.WarningDepth(depth, args...)
} else {
internal.LoggerV2Impl.Warningln(args...)
}
}
// ErrorDepth logs to the ERROR log at the specified depth.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func ErrorDepth(depth int, args ...any) {
if internal.DepthLoggerV2Impl != nil {
internal.DepthLoggerV2Impl.ErrorDepth(depth, args...)
} else {
internal.LoggerV2Impl.Errorln(args...)
}
}
// FatalDepth logs to the FATAL log at the specified depth.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func FatalDepth(depth int, args ...any) {
if internal.DepthLoggerV2Impl != nil {
internal.DepthLoggerV2Impl.FatalDepth(depth, args...)
} else {
internal.LoggerV2Impl.Fatalln(args...)
}
os.Exit(1)
}

View File

@@ -0,0 +1,26 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package internal contains functionality internal to the grpclog package.
package internal
// LoggerV2Impl is the logger used for the non-depth log functions.
var LoggerV2Impl LoggerV2
// DepthLoggerV2Impl is the logger used for the depth log functions.
var DepthLoggerV2Impl DepthLoggerV2

View File

@@ -0,0 +1,87 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package internal
// Logger mimics golang's standard Logger as an interface.
//
// Deprecated: use LoggerV2.
type Logger interface {
Fatal(args ...any)
Fatalf(format string, args ...any)
Fatalln(args ...any)
Print(args ...any)
Printf(format string, args ...any)
Println(args ...any)
}
// LoggerWrapper wraps Logger into a LoggerV2.
type LoggerWrapper struct {
Logger
}
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
func (l *LoggerWrapper) Info(args ...any) {
l.Logger.Print(args...)
}
// Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println.
func (l *LoggerWrapper) Infoln(args ...any) {
l.Logger.Println(args...)
}
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
func (l *LoggerWrapper) Infof(format string, args ...any) {
l.Logger.Printf(format, args...)
}
// Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print.
func (l *LoggerWrapper) Warning(args ...any) {
l.Logger.Print(args...)
}
// Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println.
func (l *LoggerWrapper) Warningln(args ...any) {
l.Logger.Println(args...)
}
// Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
func (l *LoggerWrapper) Warningf(format string, args ...any) {
l.Logger.Printf(format, args...)
}
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
func (l *LoggerWrapper) Error(args ...any) {
l.Logger.Print(args...)
}
// Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
func (l *LoggerWrapper) Errorln(args ...any) {
l.Logger.Println(args...)
}
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
func (l *LoggerWrapper) Errorf(format string, args ...any) {
l.Logger.Printf(format, args...)
}
// V reports whether verbosity level l is at least the requested verbose level.
func (*LoggerWrapper) V(int) bool {
// Returns true for all verbose level.
return true
}

View File

@@ -0,0 +1,267 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package internal
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
)
// LoggerV2 does underlying logging work for grpclog.
type LoggerV2 interface {
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
Info(args ...any)
// Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println.
Infoln(args ...any)
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
Infof(format string, args ...any)
// Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print.
Warning(args ...any)
// Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println.
Warningln(args ...any)
// Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
Warningf(format string, args ...any)
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
Error(args ...any)
// Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
Errorln(args ...any)
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
Errorf(format string, args ...any)
// Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print.
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
// Implementations may also call os.Exit() with a non-zero exit code.
Fatal(args ...any)
// Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
// Implementations may also call os.Exit() with a non-zero exit code.
Fatalln(args ...any)
// Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
// Implementations may also call os.Exit() with a non-zero exit code.
Fatalf(format string, args ...any)
// V reports whether verbosity level l is at least the requested verbose level.
V(l int) bool
}
// DepthLoggerV2 logs at a specified call frame. If a LoggerV2 also implements
// DepthLoggerV2, the below functions will be called with the appropriate stack
// depth set for trivial functions the logger may ignore.
//
// # Experimental
//
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
// later release.
type DepthLoggerV2 interface {
LoggerV2
// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println.
InfoDepth(depth int, args ...any)
// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println.
WarningDepth(depth int, args ...any)
// ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println.
ErrorDepth(depth int, args ...any)
// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println.
FatalDepth(depth int, args ...any)
}
const (
// infoLog indicates Info severity.
infoLog int = iota
// warningLog indicates Warning severity.
warningLog
// errorLog indicates Error severity.
errorLog
// fatalLog indicates Fatal severity.
fatalLog
)
// severityName contains the string representation of each severity.
var severityName = []string{
infoLog: "INFO",
warningLog: "WARNING",
errorLog: "ERROR",
fatalLog: "FATAL",
}
// sprintf is fmt.Sprintf.
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
var sprintf = fmt.Sprintf
// sprint is fmt.Sprint.
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
var sprint = fmt.Sprint
// sprintln is fmt.Sprintln.
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
var sprintln = fmt.Sprintln
// exit is os.Exit.
// This var exists to make it possible to test functions calling os.Exit.
var exit = os.Exit
// loggerT is the default logger used by grpclog.
type loggerT struct {
m []*log.Logger
v int
jsonFormat bool
}
func (g *loggerT) output(severity int, s string) {
sevStr := severityName[severity]
if !g.jsonFormat {
g.m[severity].Output(2, sevStr+": "+s)
return
}
// TODO: we can also include the logging component, but that needs more
// (API) changes.
b, _ := json.Marshal(map[string]string{
"severity": sevStr,
"message": s,
})
g.m[severity].Output(2, string(b))
}
func (g *loggerT) printf(severity int, format string, args ...any) {
// Note the discard check is duplicated in each print func, rather than in
// output, to avoid the expensive Sprint calls.
// De-duplicating this by moving to output would be a significant performance regression!
if lg := g.m[severity]; lg.Writer() == io.Discard {
return
}
g.output(severity, sprintf(format, args...))
}
func (g *loggerT) print(severity int, v ...any) {
if lg := g.m[severity]; lg.Writer() == io.Discard {
return
}
g.output(severity, sprint(v...))
}
func (g *loggerT) println(severity int, v ...any) {
if lg := g.m[severity]; lg.Writer() == io.Discard {
return
}
g.output(severity, sprintln(v...))
}
func (g *loggerT) Info(args ...any) {
g.print(infoLog, args...)
}
func (g *loggerT) Infoln(args ...any) {
g.println(infoLog, args...)
}
func (g *loggerT) Infof(format string, args ...any) {
g.printf(infoLog, format, args...)
}
func (g *loggerT) Warning(args ...any) {
g.print(warningLog, args...)
}
func (g *loggerT) Warningln(args ...any) {
g.println(warningLog, args...)
}
func (g *loggerT) Warningf(format string, args ...any) {
g.printf(warningLog, format, args...)
}
func (g *loggerT) Error(args ...any) {
g.print(errorLog, args...)
}
func (g *loggerT) Errorln(args ...any) {
g.println(errorLog, args...)
}
func (g *loggerT) Errorf(format string, args ...any) {
g.printf(errorLog, format, args...)
}
func (g *loggerT) Fatal(args ...any) {
g.print(fatalLog, args...)
exit(1)
}
func (g *loggerT) Fatalln(args ...any) {
g.println(fatalLog, args...)
exit(1)
}
func (g *loggerT) Fatalf(format string, args ...any) {
g.printf(fatalLog, format, args...)
exit(1)
}
func (g *loggerT) V(l int) bool {
return l <= g.v
}
// LoggerV2Config configures the LoggerV2 implementation.
type LoggerV2Config struct {
// Verbosity sets the verbosity level of the logger.
Verbosity int
// FormatJSON controls whether the logger should output logs in JSON format.
FormatJSON bool
}
// combineLoggers returns a combined logger for both higher & lower severity logs,
// or only one if the other is io.Discard.
//
// This uses io.Discard instead of io.MultiWriter when all loggers
// are set to io.Discard. Both this package and the standard log package have
// significant optimizations for io.Discard, which io.MultiWriter lacks (as of
// this writing).
func combineLoggers(lower, higher io.Writer) io.Writer {
if lower == io.Discard {
return higher
}
if higher == io.Discard {
return lower
}
return io.MultiWriter(lower, higher)
}
// NewLoggerV2 creates a new LoggerV2 instance with the provided configuration.
// The infoW, warningW, and errorW writers are used to write log messages of
// different severity levels.
func NewLoggerV2(infoW, warningW, errorW io.Writer, c LoggerV2Config) LoggerV2 {
flag := log.LstdFlags
if c.FormatJSON {
flag = 0
}
warningW = combineLoggers(infoW, warningW)
errorW = combineLoggers(errorW, warningW)
fatalW := errorW
m := []*log.Logger{
log.New(infoW, "", flag),
log.New(warningW, "", flag),
log.New(errorW, "", flag),
log.New(fatalW, "", flag),
}
return &loggerT{m: m, v: c.Verbosity, jsonFormat: c.FormatJSON}
}

34
vendor/google.golang.org/grpc/grpclog/logger.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpclog
import "google.golang.org/grpc/grpclog/internal"
// Logger mimics golang's standard Logger as an interface.
//
// Deprecated: use LoggerV2.
type Logger internal.Logger
// SetLogger sets the logger that is used in grpc. Call only from
// init() functions.
//
// Deprecated: use SetLoggerV2.
func SetLogger(l Logger) {
internal.LoggerV2Impl = &internal.LoggerWrapper{Logger: l}
}

97
vendor/google.golang.org/grpc/grpclog/loggerv2.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpclog
import (
"io"
"os"
"strconv"
"strings"
"google.golang.org/grpc/grpclog/internal"
)
// LoggerV2 does underlying logging work for grpclog.
type LoggerV2 internal.LoggerV2
// SetLoggerV2 sets logger that is used in grpc to a V2 logger.
// Not mutex-protected, should be called before any gRPC functions.
func SetLoggerV2(l LoggerV2) {
if _, ok := l.(*componentData); ok {
panic("cannot use component logger as grpclog logger")
}
internal.LoggerV2Impl = l
internal.DepthLoggerV2Impl, _ = l.(internal.DepthLoggerV2)
}
// NewLoggerV2 creates a loggerV2 with the provided writers.
// Fatal logs will be written to errorW, warningW, infoW, followed by exit(1).
// Error logs will be written to errorW, warningW and infoW.
// Warning logs will be written to warningW and infoW.
// Info logs will be written to infoW.
func NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 {
return internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{})
}
// NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and
// verbosity level.
func NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 {
return internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{Verbosity: v})
}
// newLoggerV2 creates a loggerV2 to be used as default logger.
// All logs are written to stderr.
func newLoggerV2() LoggerV2 {
errorW := io.Discard
warningW := io.Discard
infoW := io.Discard
logLevel := os.Getenv("GRPC_GO_LOG_SEVERITY_LEVEL")
switch logLevel {
case "", "ERROR", "error": // If env is unset, set level to ERROR.
errorW = os.Stderr
case "WARNING", "warning":
warningW = os.Stderr
case "INFO", "info":
infoW = os.Stderr
}
var v int
vLevel := os.Getenv("GRPC_GO_LOG_VERBOSITY_LEVEL")
if vl, err := strconv.Atoi(vLevel); err == nil {
v = vl
}
jsonFormat := strings.EqualFold(os.Getenv("GRPC_GO_LOG_FORMATTER"), "json")
return internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{
Verbosity: v,
FormatJSON: jsonFormat,
})
}
// DepthLoggerV2 logs at a specified call frame. If a LoggerV2 also implements
// DepthLoggerV2, the below functions will be called with the appropriate stack
// depth set for trivial functions the logger may ignore.
//
// # Experimental
//
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
// later release.
type DepthLoggerV2 internal.DepthLoggerV2

104
vendor/google.golang.org/grpc/interceptor.go generated vendored Normal file
View File

@@ -0,0 +1,104 @@
/*
*
* Copyright 2016 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package grpc
import (
"context"
)
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
type UnaryInvoker func(ctx context.Context, method string, req, reply any, cc *ClientConn, opts ...CallOption) error
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client.
// Unary interceptors can be specified as a DialOption, using
// WithUnaryInterceptor() or WithChainUnaryInterceptor(), when creating a
// ClientConn. When a unary interceptor(s) is set on a ClientConn, gRPC
// delegates all unary RPC invocations to the interceptor, and it is the
// responsibility of the interceptor to call invoker to complete the processing
// of the RPC.
//
// method is the RPC name. req and reply are the corresponding request and
// response messages. cc is the ClientConn on which the RPC was invoked. invoker
// is the handler to complete the RPC and it is the responsibility of the
// interceptor to call it. opts contain all applicable call options, including
// defaults from the ClientConn as well as per-call options.
//
// The returned error must be compatible with the status package.
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply any, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
// Streamer is called by StreamClientInterceptor to create a ClientStream.
type Streamer func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error)
// StreamClientInterceptor intercepts the creation of a ClientStream. Stream
// interceptors can be specified as a DialOption, using WithStreamInterceptor()
// or WithChainStreamInterceptor(), when creating a ClientConn. When a stream
// interceptor(s) is set on the ClientConn, gRPC delegates all stream creations
// to the interceptor, and it is the responsibility of the interceptor to call
// streamer.
//
// desc contains a description of the stream. cc is the ClientConn on which the
// RPC was invoked. streamer is the handler to create a ClientStream and it is
// the responsibility of the interceptor to call it. opts contain all applicable
// call options, including defaults from the ClientConn as well as per-call
// options.
//
// StreamClientInterceptor may return a custom ClientStream to intercept all I/O
// operations. The returned error must be compatible with the status package.
type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)
// UnaryServerInfo consists of various information about a unary RPC on
// server side. All per-rpc information may be mutated by the interceptor.
type UnaryServerInfo struct {
// Server is the service implementation the user provides. This is read-only.
Server any
// FullMethod is the full RPC method string, i.e., /package.service/method.
FullMethod string
}
// UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal
// execution of a unary RPC.
//
// If a UnaryHandler returns an error, it should either be produced by the
// status package, or be one of the context errors. Otherwise, gRPC will use
// codes.Unknown as the status code and err.Error() as the status message of the
// RPC.
type UnaryHandler func(ctx context.Context, req any) (any, error)
// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info
// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper
// of the service method implementation. It is the responsibility of the interceptor to invoke handler
// to complete the RPC.
type UnaryServerInterceptor func(ctx context.Context, req any, info *UnaryServerInfo, handler UnaryHandler) (resp any, err error)
// StreamServerInfo consists of various information about a streaming RPC on
// server side. All per-rpc information may be mutated by the interceptor.
type StreamServerInfo struct {
// FullMethod is the full RPC method string, i.e., /package.service/method.
FullMethod string
// IsClientStream indicates whether the RPC is a client streaming RPC.
IsClientStream bool
// IsServerStream indicates whether the RPC is a server streaming RPC.
IsServerStream bool
}
// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server.
// info contains all the information of this RPC the interceptor can operate on. And handler is the
// service method implementation. It is the responsibility of the interceptor to invoke handler to
// complete the RPC.
type StreamServerInterceptor func(srv any, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error

View File

@@ -0,0 +1,109 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package backoff implement the backoff strategy for gRPC.
//
// This is kept in internal until the gRPC project decides whether or not to
// allow alternative backoff strategies.
package backoff
import (
"context"
"errors"
rand "math/rand/v2"
"time"
grpcbackoff "google.golang.org/grpc/backoff"
)
// Strategy defines the methodology for backing off after a grpc connection
// failure.
type Strategy interface {
// Backoff returns the amount of time to wait before the next retry given
// the number of consecutive failures.
Backoff(retries int) time.Duration
}
// DefaultExponential is an exponential backoff implementation using the
// default values for all the configurable knobs defined in
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
var DefaultExponential = Exponential{Config: grpcbackoff.DefaultConfig}
// Exponential implements exponential backoff algorithm as defined in
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
type Exponential struct {
// Config contains all options to configure the backoff algorithm.
Config grpcbackoff.Config
}
// Backoff returns the amount of time to wait before the next retry given the
// number of retries.
func (bc Exponential) Backoff(retries int) time.Duration {
if retries == 0 {
return bc.Config.BaseDelay
}
backoff, max := float64(bc.Config.BaseDelay), float64(bc.Config.MaxDelay)
for backoff < max && retries > 0 {
backoff *= bc.Config.Multiplier
retries--
}
if backoff > max {
backoff = max
}
// Randomize backoff delays so that if a cluster of requests start at
// the same time, they won't operate in lockstep.
backoff *= 1 + bc.Config.Jitter*(rand.Float64()*2-1)
if backoff < 0 {
return 0
}
return time.Duration(backoff)
}
// ErrResetBackoff is the error to be returned by the function executed by RunF,
// to instruct the latter to reset its backoff state.
var ErrResetBackoff = errors.New("reset backoff state")
// RunF provides a convenient way to run a function f repeatedly until the
// context expires or f returns a non-nil error that is not ErrResetBackoff.
// When f returns ErrResetBackoff, RunF continues to run f, but resets its
// backoff state before doing so. backoff accepts an integer representing the
// number of retries, and returns the amount of time to backoff.
func RunF(ctx context.Context, f func() error, backoff func(int) time.Duration) {
attempt := 0
timer := time.NewTimer(0)
for ctx.Err() == nil {
select {
case <-timer.C:
case <-ctx.Done():
timer.Stop()
return
}
err := f()
if errors.Is(err, ErrResetBackoff) {
timer.Reset(0)
attempt = 0
continue
}
if err != nil {
return
}
timer.Reset(backoff(attempt))
attempt++
}
}

View File

@@ -0,0 +1,84 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package gracefulswitch
import (
"encoding/json"
"fmt"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/serviceconfig"
)
type lbConfig struct {
serviceconfig.LoadBalancingConfig
childBuilder balancer.Builder
childConfig serviceconfig.LoadBalancingConfig
}
// ChildName returns the name of the child balancer of the gracefulswitch
// Balancer.
func ChildName(l serviceconfig.LoadBalancingConfig) string {
return l.(*lbConfig).childBuilder.Name()
}
// ParseConfig parses a child config list and returns a LB config for the
// gracefulswitch Balancer.
//
// cfg is expected to be a json.RawMessage containing a JSON array of LB policy
// names + configs as the format of the "loadBalancingConfig" field in
// ServiceConfig. It returns a type that should be passed to
// UpdateClientConnState in the BalancerConfig field.
func ParseConfig(cfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
var lbCfg []map[string]json.RawMessage
if err := json.Unmarshal(cfg, &lbCfg); err != nil {
return nil, err
}
for i, e := range lbCfg {
if len(e) != 1 {
return nil, fmt.Errorf("expected a JSON struct with one entry; received entry %v at index %d", e, i)
}
var name string
var jsonCfg json.RawMessage
for name, jsonCfg = range e {
}
builder := balancer.Get(name)
if builder == nil {
// Skip unregistered balancer names.
continue
}
parser, ok := builder.(balancer.ConfigParser)
if !ok {
// This is a valid child with no config.
return &lbConfig{childBuilder: builder}, nil
}
cfg, err := parser.ParseConfig(jsonCfg)
if err != nil {
return nil, fmt.Errorf("error parsing config for policy %q: %v", name, err)
}
return &lbConfig{childBuilder: builder, childConfig: cfg}, nil
}
return nil, fmt.Errorf("no supported policies found in config: %v", string(cfg))
}

View File

@@ -0,0 +1,421 @@
/*
*
* Copyright 2022 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package gracefulswitch implements a graceful switch load balancer.
package gracefulswitch
import (
"errors"
"fmt"
"sync"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/resolver"
)
var errBalancerClosed = errors.New("gracefulSwitchBalancer is closed")
var _ balancer.Balancer = (*Balancer)(nil)
// NewBalancer returns a graceful switch Balancer.
func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions) *Balancer {
return &Balancer{
cc: cc,
bOpts: opts,
}
}
// Balancer is a utility to gracefully switch from one balancer to
// a new balancer. It implements the balancer.Balancer interface.
type Balancer struct {
bOpts balancer.BuildOptions
cc balancer.ClientConn
// mu protects the following fields and all fields within balancerCurrent
// and balancerPending. mu does not need to be held when calling into the
// child balancers, as all calls into these children happen only as a direct
// result of a call into the gracefulSwitchBalancer, which are also
// guaranteed to be synchronous. There is one exception: an UpdateState call
// from a child balancer when current and pending are populated can lead to
// calling Close() on the current. To prevent that racing with an
// UpdateSubConnState from the channel, we hold currentMu during Close and
// UpdateSubConnState calls.
mu sync.Mutex
balancerCurrent *balancerWrapper
balancerPending *balancerWrapper
closed bool // set to true when this balancer is closed
// currentMu must be locked before mu. This mutex guards against this
// sequence of events: UpdateSubConnState() called, finds the
// balancerCurrent, gives up lock, updateState comes in, causes Close() on
// balancerCurrent before the UpdateSubConnState is called on the
// balancerCurrent.
currentMu sync.Mutex
// activeGoroutines tracks all the goroutines that this balancer has started
// and that should be waited on when the balancer closes.
activeGoroutines sync.WaitGroup
}
// swap swaps out the current lb with the pending lb and updates the ClientConn.
// The caller must hold gsb.mu.
func (gsb *Balancer) swap() {
gsb.cc.UpdateState(gsb.balancerPending.lastState)
cur := gsb.balancerCurrent
gsb.balancerCurrent = gsb.balancerPending
gsb.balancerPending = nil
gsb.activeGoroutines.Add(1)
go func() {
defer gsb.activeGoroutines.Done()
gsb.currentMu.Lock()
defer gsb.currentMu.Unlock()
cur.Close()
}()
}
// Helper function that checks if the balancer passed in is current or pending.
// The caller must hold gsb.mu.
func (gsb *Balancer) balancerCurrentOrPending(bw *balancerWrapper) bool {
return bw == gsb.balancerCurrent || bw == gsb.balancerPending
}
// SwitchTo initializes the graceful switch process, which completes based on
// connectivity state changes on the current/pending balancer. Thus, the switch
// process is not complete when this method returns. This method must be called
// synchronously alongside the rest of the balancer.Balancer methods this
// Graceful Switch Balancer implements.
//
// Deprecated: use ParseConfig and pass a parsed config to UpdateClientConnState
// to cause the Balancer to automatically change to the new child when necessary.
func (gsb *Balancer) SwitchTo(builder balancer.Builder) error {
_, err := gsb.switchTo(builder)
return err
}
func (gsb *Balancer) switchTo(builder balancer.Builder) (*balancerWrapper, error) {
gsb.mu.Lock()
if gsb.closed {
gsb.mu.Unlock()
return nil, errBalancerClosed
}
bw := &balancerWrapper{
ClientConn: gsb.cc,
builder: builder,
gsb: gsb,
lastState: balancer.State{
ConnectivityState: connectivity.Connecting,
Picker: base.NewErrPicker(balancer.ErrNoSubConnAvailable),
},
subconns: make(map[balancer.SubConn]bool),
}
balToClose := gsb.balancerPending // nil if there is no pending balancer
if gsb.balancerCurrent == nil {
gsb.balancerCurrent = bw
} else {
gsb.balancerPending = bw
}
gsb.mu.Unlock()
balToClose.Close()
// This function takes a builder instead of a balancer because builder.Build
// can call back inline, and this utility needs to handle the callbacks.
newBalancer := builder.Build(bw, gsb.bOpts)
if newBalancer == nil {
// This is illegal and should never happen; we clear the balancerWrapper
// we were constructing if it happens to avoid a potential panic.
gsb.mu.Lock()
if gsb.balancerPending != nil {
gsb.balancerPending = nil
} else {
gsb.balancerCurrent = nil
}
gsb.mu.Unlock()
return nil, balancer.ErrBadResolverState
}
// This write doesn't need to take gsb.mu because this field never gets read
// or written to on any calls from the current or pending. Calls from grpc
// to this balancer are guaranteed to be called synchronously, so this
// bw.Balancer field will never be forwarded to until this SwitchTo()
// function returns.
bw.Balancer = newBalancer
return bw, nil
}
// Returns nil if the graceful switch balancer is closed.
func (gsb *Balancer) latestBalancer() *balancerWrapper {
gsb.mu.Lock()
defer gsb.mu.Unlock()
if gsb.balancerPending != nil {
return gsb.balancerPending
}
return gsb.balancerCurrent
}
// UpdateClientConnState forwards the update to the latest balancer created.
//
// If the state's BalancerConfig is the config returned by a call to
// gracefulswitch.ParseConfig, then this function will automatically SwitchTo
// the balancer indicated by the config before forwarding its config to it, if
// necessary.
func (gsb *Balancer) UpdateClientConnState(state balancer.ClientConnState) error {
// The resolver data is only relevant to the most recent LB Policy.
balToUpdate := gsb.latestBalancer()
gsbCfg, ok := state.BalancerConfig.(*lbConfig)
if ok {
// Switch to the child in the config unless it is already active.
if balToUpdate == nil || gsbCfg.childBuilder.Name() != balToUpdate.builder.Name() {
var err error
balToUpdate, err = gsb.switchTo(gsbCfg.childBuilder)
if err != nil {
return fmt.Errorf("could not switch to new child balancer: %w", err)
}
}
// Unwrap the child balancer's config.
state.BalancerConfig = gsbCfg.childConfig
}
if balToUpdate == nil {
return errBalancerClosed
}
// Perform this call without gsb.mu to prevent deadlocks if the child calls
// back into the channel. The latest balancer can never be closed during a
// call from the channel, even without gsb.mu held.
return balToUpdate.UpdateClientConnState(state)
}
// ResolverError forwards the error to the latest balancer created.
func (gsb *Balancer) ResolverError(err error) {
// The resolver data is only relevant to the most recent LB Policy.
balToUpdate := gsb.latestBalancer()
if balToUpdate == nil {
gsb.cc.UpdateState(balancer.State{
ConnectivityState: connectivity.TransientFailure,
Picker: base.NewErrPicker(err),
})
return
}
// Perform this call without gsb.mu to prevent deadlocks if the child calls
// back into the channel. The latest balancer can never be closed during a
// call from the channel, even without gsb.mu held.
balToUpdate.ResolverError(err)
}
// ExitIdle forwards the call to the latest balancer created.
//
// If the latest balancer does not support ExitIdle, the subConns are
// re-connected to manually.
func (gsb *Balancer) ExitIdle() {
balToUpdate := gsb.latestBalancer()
if balToUpdate == nil {
return
}
// There is no need to protect this read with a mutex, as the write to the
// Balancer field happens in SwitchTo, which completes before this can be
// called.
balToUpdate.ExitIdle()
}
// updateSubConnState forwards the update to the appropriate child.
func (gsb *Balancer) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState, cb func(balancer.SubConnState)) {
gsb.currentMu.Lock()
defer gsb.currentMu.Unlock()
gsb.mu.Lock()
// Forward update to the appropriate child. Even if there is a pending
// balancer, the current balancer should continue to get SubConn updates to
// maintain the proper state while the pending is still connecting.
var balToUpdate *balancerWrapper
if gsb.balancerCurrent != nil && gsb.balancerCurrent.subconns[sc] {
balToUpdate = gsb.balancerCurrent
} else if gsb.balancerPending != nil && gsb.balancerPending.subconns[sc] {
balToUpdate = gsb.balancerPending
}
if balToUpdate == nil {
// SubConn belonged to a stale lb policy that has not yet fully closed,
// or the balancer was already closed.
gsb.mu.Unlock()
return
}
if state.ConnectivityState == connectivity.Shutdown {
delete(balToUpdate.subconns, sc)
}
gsb.mu.Unlock()
if cb != nil {
cb(state)
} else {
balToUpdate.UpdateSubConnState(sc, state)
}
}
// UpdateSubConnState forwards the update to the appropriate child.
func (gsb *Balancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
gsb.updateSubConnState(sc, state, nil)
}
// Close closes any active child balancers.
func (gsb *Balancer) Close() {
gsb.mu.Lock()
gsb.closed = true
currentBalancerToClose := gsb.balancerCurrent
gsb.balancerCurrent = nil
pendingBalancerToClose := gsb.balancerPending
gsb.balancerPending = nil
gsb.mu.Unlock()
currentBalancerToClose.Close()
pendingBalancerToClose.Close()
gsb.activeGoroutines.Wait()
}
// balancerWrapper wraps a balancer.Balancer, and overrides some Balancer
// methods to help cleanup SubConns created by the wrapped balancer.
//
// It implements the balancer.ClientConn interface and is passed down in that
// capacity to the wrapped balancer. It maintains a set of subConns created by
// the wrapped balancer and calls from the latter to create/update/shutdown
// SubConns update this set before being forwarded to the parent ClientConn.
// State updates from the wrapped balancer can result in invocation of the
// graceful switch logic.
type balancerWrapper struct {
balancer.ClientConn
balancer.Balancer
gsb *Balancer
builder balancer.Builder
lastState balancer.State
subconns map[balancer.SubConn]bool // subconns created by this balancer
}
// Close closes the underlying LB policy and shuts down the subconns it
// created. bw must not be referenced via balancerCurrent or balancerPending in
// gsb when called. gsb.mu must not be held. Does not panic with a nil
// receiver.
func (bw *balancerWrapper) Close() {
// before Close is called.
if bw == nil {
return
}
// There is no need to protect this read with a mutex, as Close() is
// impossible to be called concurrently with the write in SwitchTo(). The
// callsites of Close() for this balancer in Graceful Switch Balancer will
// never be called until SwitchTo() returns.
bw.Balancer.Close()
bw.gsb.mu.Lock()
for sc := range bw.subconns {
sc.Shutdown()
}
bw.gsb.mu.Unlock()
}
func (bw *balancerWrapper) UpdateState(state balancer.State) {
// Hold the mutex for this entire call to ensure it cannot occur
// concurrently with other updateState() calls. This causes updates to
// lastState and calls to cc.UpdateState to happen atomically.
bw.gsb.mu.Lock()
defer bw.gsb.mu.Unlock()
bw.lastState = state
// If Close() acquires the mutex before UpdateState(), the balancer
// will already have been removed from the current or pending state when
// reaching this point.
if !bw.gsb.balancerCurrentOrPending(bw) {
// Returning here ensures that (*Balancer).swap() is not invoked after
// (*Balancer).Close() and therefore prevents "use after close".
return
}
if bw == bw.gsb.balancerCurrent {
// In the case that the current balancer exits READY, and there is a pending
// balancer, you can forward the pending balancer's cached State up to
// ClientConn and swap the pending into the current. This is because there
// is no reason to gracefully switch from and keep using the old policy as
// the ClientConn is not connected to any backends.
if state.ConnectivityState != connectivity.Ready && bw.gsb.balancerPending != nil {
bw.gsb.swap()
return
}
// Even if there is a pending balancer waiting to be gracefully switched to,
// continue to forward current balancer updates to the Client Conn. Ignoring
// state + picker from the current would cause undefined behavior/cause the
// system to behave incorrectly from the current LB policies perspective.
// Also, the current LB is still being used by grpc to choose SubConns per
// RPC, and thus should use the most updated form of the current balancer.
bw.gsb.cc.UpdateState(state)
return
}
// This method is now dealing with a state update from the pending balancer.
// If the current balancer is currently in a state other than READY, the new
// policy can be swapped into place immediately. This is because there is no
// reason to gracefully switch from and keep using the old policy as the
// ClientConn is not connected to any backends.
if state.ConnectivityState != connectivity.Connecting || bw.gsb.balancerCurrent.lastState.ConnectivityState != connectivity.Ready {
bw.gsb.swap()
}
}
func (bw *balancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
bw.gsb.mu.Lock()
if !bw.gsb.balancerCurrentOrPending(bw) {
bw.gsb.mu.Unlock()
return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw)
}
bw.gsb.mu.Unlock()
var sc balancer.SubConn
oldListener := opts.StateListener
opts.StateListener = func(state balancer.SubConnState) { bw.gsb.updateSubConnState(sc, state, oldListener) }
sc, err := bw.gsb.cc.NewSubConn(addrs, opts)
if err != nil {
return nil, err
}
bw.gsb.mu.Lock()
if !bw.gsb.balancerCurrentOrPending(bw) { // balancer was closed during this call
sc.Shutdown()
bw.gsb.mu.Unlock()
return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw)
}
bw.subconns[sc] = true
bw.gsb.mu.Unlock()
return sc, nil
}
func (bw *balancerWrapper) ResolveNow(opts resolver.ResolveNowOptions) {
// Ignore ResolveNow requests from anything other than the most recent
// balancer, because older balancers were already removed from the config.
if bw != bw.gsb.latestBalancer() {
return
}
bw.gsb.cc.ResolveNow(opts)
}
func (bw *balancerWrapper) RemoveSubConn(sc balancer.SubConn) {
// Note: existing third party balancers may call this, so it must remain
// until RemoveSubConn is fully removed.
sc.Shutdown()
}
func (bw *balancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
bw.gsb.mu.Lock()
if !bw.gsb.balancerCurrentOrPending(bw) {
bw.gsb.mu.Unlock()
return
}
bw.gsb.mu.Unlock()
bw.gsb.cc.UpdateAddresses(sc, addrs)
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package balancerload defines APIs to parse server loads in trailers. The
// parsed loads are sent to balancers in DoneInfo.
package balancerload
import (
"google.golang.org/grpc/metadata"
)
// Parser converts loads from metadata into a concrete type.
type Parser interface {
// Parse parses loads from metadata.
Parse(md metadata.MD) any
}
var parser Parser
// SetParser sets the load parser.
//
// Not mutex-protected, should be called before any gRPC functions.
func SetParser(lr Parser) {
parser = lr
}
// Parse calls parser.Read().
func Parse(md metadata.MD) any {
if parser == nil {
return nil
}
return parser.Parse(md)
}

View File

@@ -0,0 +1,192 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package binarylog implementation binary logging as defined in
// https://github.com/grpc/proposal/blob/master/A16-binary-logging.md.
package binarylog
import (
"fmt"
"os"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/grpcutil"
)
var grpclogLogger = grpclog.Component("binarylog")
// Logger specifies MethodLoggers for method names with a Log call that
// takes a context.
//
// This is used in the 1.0 release of gcp/observability, and thus must not be
// deleted or changed.
type Logger interface {
GetMethodLogger(methodName string) MethodLogger
}
// binLogger is the global binary logger for the binary. One of this should be
// built at init time from the configuration (environment variable or flags).
//
// It is used to get a MethodLogger for each individual method.
var binLogger Logger
// SetLogger sets the binary logger.
//
// Only call this at init time.
func SetLogger(l Logger) {
binLogger = l
}
// GetLogger gets the binary logger.
//
// Only call this at init time.
func GetLogger() Logger {
return binLogger
}
// GetMethodLogger returns the MethodLogger for the given methodName.
//
// methodName should be in the format of "/service/method".
//
// Each MethodLogger returned by this method is a new instance. This is to
// generate sequence id within the call.
func GetMethodLogger(methodName string) MethodLogger {
if binLogger == nil {
return nil
}
return binLogger.GetMethodLogger(methodName)
}
func init() {
const envStr = "GRPC_BINARY_LOG_FILTER"
configStr := os.Getenv(envStr)
binLogger = NewLoggerFromConfigString(configStr)
}
// MethodLoggerConfig contains the setting for logging behavior of a method
// logger. Currently, it contains the max length of header and message.
type MethodLoggerConfig struct {
// Max length of header and message.
Header, Message uint64
}
// LoggerConfig contains the config for loggers to create method loggers.
type LoggerConfig struct {
All *MethodLoggerConfig
Services map[string]*MethodLoggerConfig
Methods map[string]*MethodLoggerConfig
Blacklist map[string]struct{}
}
type logger struct {
config LoggerConfig
}
// NewLoggerFromConfig builds a logger with the given LoggerConfig.
func NewLoggerFromConfig(config LoggerConfig) Logger {
return &logger{config: config}
}
// newEmptyLogger creates an empty logger. The map fields need to be filled in
// using the set* functions.
func newEmptyLogger() *logger {
return &logger{}
}
// Set method logger for "*".
func (l *logger) setDefaultMethodLogger(ml *MethodLoggerConfig) error {
if l.config.All != nil {
return fmt.Errorf("conflicting global rules found")
}
l.config.All = ml
return nil
}
// Set method logger for "service/*".
//
// New MethodLogger with same service overrides the old one.
func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig) error {
if _, ok := l.config.Services[service]; ok {
return fmt.Errorf("conflicting service rules for service %v found", service)
}
if l.config.Services == nil {
l.config.Services = make(map[string]*MethodLoggerConfig)
}
l.config.Services[service] = ml
return nil
}
// Set method logger for "service/method".
//
// New MethodLogger with same method overrides the old one.
func (l *logger) setMethodMethodLogger(method string, ml *MethodLoggerConfig) error {
if _, ok := l.config.Blacklist[method]; ok {
return fmt.Errorf("conflicting blacklist rules for method %v found", method)
}
if _, ok := l.config.Methods[method]; ok {
return fmt.Errorf("conflicting method rules for method %v found", method)
}
if l.config.Methods == nil {
l.config.Methods = make(map[string]*MethodLoggerConfig)
}
l.config.Methods[method] = ml
return nil
}
// Set blacklist method for "-service/method".
func (l *logger) setBlacklist(method string) error {
if _, ok := l.config.Blacklist[method]; ok {
return fmt.Errorf("conflicting blacklist rules for method %v found", method)
}
if _, ok := l.config.Methods[method]; ok {
return fmt.Errorf("conflicting method rules for method %v found", method)
}
if l.config.Blacklist == nil {
l.config.Blacklist = make(map[string]struct{})
}
l.config.Blacklist[method] = struct{}{}
return nil
}
// getMethodLogger returns the MethodLogger for the given methodName.
//
// methodName should be in the format of "/service/method".
//
// Each MethodLogger returned by this method is a new instance. This is to
// generate sequence id within the call.
func (l *logger) GetMethodLogger(methodName string) MethodLogger {
s, m, err := grpcutil.ParseMethod(methodName)
if err != nil {
grpclogLogger.Infof("binarylogging: failed to parse %q: %v", methodName, err)
return nil
}
if ml, ok := l.config.Methods[s+"/"+m]; ok {
return NewTruncatingMethodLogger(ml.Header, ml.Message)
}
if _, ok := l.config.Blacklist[s+"/"+m]; ok {
return nil
}
if ml, ok := l.config.Services[s]; ok {
return NewTruncatingMethodLogger(ml.Header, ml.Message)
}
if l.config.All == nil {
return nil
}
return NewTruncatingMethodLogger(l.config.All.Header, l.config.All.Message)
}

View File

@@ -0,0 +1,42 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// This file contains exported variables/functions that are exported for testing
// only.
//
// An ideal way for this would be to put those in a *_test.go but in binarylog
// package. But this doesn't work with staticcheck with go module. Error was:
// "MdToMetadataProto not declared by package binarylog". This could be caused
// by the way staticcheck looks for files for a certain package, which doesn't
// support *_test.go files.
//
// Move those to binary_test.go when staticcheck is fixed.
package binarylog
var (
// AllLogger is a logger that logs all headers/messages for all RPCs. It's
// for testing only.
AllLogger = NewLoggerFromConfigString("*")
// MdToMetadataProto converts metadata to a binary logging proto message.
// It's for testing only.
MdToMetadataProto = mdToMetadataProto
// AddrToProto converts an address to a binary logging proto message. It's
// for testing only.
AddrToProto = addrToProto
)

Some files were not shown because too many files have changed in this diff Show More