diff --git a/go.mod b/go.mod index 3798e9f50..8570c6ef1 100644 --- a/go.mod +++ b/go.mod @@ -91,7 +91,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/gax-go/v2 v2.14.2 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -121,8 +121,8 @@ require ( golang.org/x/sys v0.34.0 golang.org/x/text v0.27.0 // indirect golang.org/x/time v0.12.0 // indirect - google.golang.org/api v0.242.0 - google.golang.org/genproto v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/api v0.243.0 + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect @@ -130,7 +130,7 @@ require ( require ( cel.dev/expr v0.24.0 // indirect - cloud.google.com/go/auth v0.16.2 // indirect + cloud.google.com/go/auth v0.16.3 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect dario.cat/mergo v1.0.2 // indirect @@ -202,8 +202,8 @@ require ( go.opentelemetry.io/otel/trace v1.36.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c3e6bd962..0d0874d8d 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.121.1 h1:S3kTQSydxmu1JfLRLpKtxRPA7rSrYPRPEUmL/PavVUw= cloud.google.com/go v0.121.1/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw= -cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4= -cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA= +cloud.google.com/go/auth v0.16.3 h1:kabzoQ9/bobUmnseYnBO6qQG7q4a/CffFRlJSxv2wCc= +cloud.google.com/go/auth v0.16.3/go.mod h1:NucRGjaXfzP1ltpcQ7On/VTZ0H4kWB5Jy+Y9Dnm76fA= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= @@ -308,8 +308,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= -github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -636,19 +636,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.242.0 h1:7Lnb1nfnpvbkCiZek6IXKdJ0MFuAZNAJKQfA1ws62xg= -google.golang.org/api v0.242.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= +google.golang.org/api v0.243.0 h1:sw+ESIJ4BVnlJcWu9S+p2Z6Qq1PjG77T8IJ1xtp4jZQ= +google.golang.org/api v0.243.0/go.mod h1:GE4QtYfaybx1KmeHMdBnNnyLzBZCVihGBXAmJu/uUr8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20250519155744-55703ea1f237 h1:2zGWyk04EwQ3mmV4dd4M4U7P/igHi5p7CBJEg1rI6A8= -google.golang.org/genproto v0.0.0-20250519155744-55703ea1f237/go.mod h1:LhI4bRmX3rqllzQ+BGneexULkEjBf2gsAfkbeCA8IbU= -google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= -google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= diff --git a/vendor/cloud.google.com/go/auth/CHANGES.md b/vendor/cloud.google.com/go/auth/CHANGES.md index 66131916e..cbee12816 100644 --- a/vendor/cloud.google.com/go/auth/CHANGES.md +++ b/vendor/cloud.google.com/go/auth/CHANGES.md @@ -1,5 +1,12 @@ # Changelog +## [0.16.3](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.2...auth/v0.16.3) (2025-07-17) + + +### Bug Fixes + +* **auth:** Fix race condition in cachedTokenProvider.tokenAsync ([#12586](https://github.com/googleapis/google-cloud-go/issues/12586)) ([73867cc](https://github.com/googleapis/google-cloud-go/commit/73867ccc1e9808d65361bcfc0776bd95fe34dbb3)) + ## [0.16.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.1...auth/v0.16.2) (2025-06-04) diff --git a/vendor/cloud.google.com/go/auth/auth.go b/vendor/cloud.google.com/go/auth/auth.go index cd5e98868..fb24c43eb 100644 --- a/vendor/cloud.google.com/go/auth/auth.go +++ b/vendor/cloud.google.com/go/auth/auth.go @@ -362,9 +362,6 @@ func (c *cachedTokenProvider) tokenState() tokenState { // blocking call to Token should likely return the same error on the main goroutine. func (c *cachedTokenProvider) tokenAsync(ctx context.Context) { fn := func() { - c.mu.Lock() - c.isRefreshRunning = true - c.mu.Unlock() t, err := c.tp.Token(ctx) c.mu.Lock() defer c.mu.Unlock() @@ -380,6 +377,7 @@ func (c *cachedTokenProvider) tokenAsync(ctx context.Context) { c.mu.Lock() defer c.mu.Unlock() if !c.isRefreshRunning && !c.isRefreshErr { + c.isRefreshRunning = true go fn() } } diff --git a/vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.json b/vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.json index 846e3ece8..2fcff6e27 100644 --- a/vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.json +++ b/vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.json @@ -1,3 +1,3 @@ { - "v2": "2.14.2" + "v2": "2.15.0" } diff --git a/vendor/github.com/googleapis/gax-go/v2/CHANGES.md b/vendor/github.com/googleapis/gax-go/v2/CHANGES.md index a7fe145a4..fec6b1da9 100644 --- a/vendor/github.com/googleapis/gax-go/v2/CHANGES.md +++ b/vendor/github.com/googleapis/gax-go/v2/CHANGES.md @@ -1,5 +1,12 @@ # Changelog +## [2.15.0](https://github.com/googleapis/gax-go/compare/v2.14.2...v2.15.0) (2025-07-09) + + +### Features + +* **apierror:** improve gRPC status code mapping for HTTP errors ([#431](https://github.com/googleapis/gax-go/issues/431)) ([c207f2a](https://github.com/googleapis/gax-go/commit/c207f2a19ab91d3baee458b57d4aa992519025c7)) + ## [2.14.2](https://github.com/googleapis/gax-go/compare/v2.14.1...v2.14.2) (2025-05-12) diff --git a/vendor/github.com/googleapis/gax-go/v2/apierror/apierror.go b/vendor/github.com/googleapis/gax-go/v2/apierror/apierror.go index 7de60773d..90a40d29c 100644 --- a/vendor/github.com/googleapis/gax-go/v2/apierror/apierror.go +++ b/vendor/github.com/googleapis/gax-go/v2/apierror/apierror.go @@ -38,6 +38,7 @@ package apierror import ( "errors" "fmt" + "net/http" "strings" jsonerror "github.com/googleapis/gax-go/v2/apierror/internal/proto" @@ -49,6 +50,39 @@ import ( "google.golang.org/protobuf/proto" ) +// canonicalMap maps HTTP codes to gRPC status code equivalents. +var canonicalMap = map[int]codes.Code{ + http.StatusOK: codes.OK, + http.StatusBadRequest: codes.InvalidArgument, + http.StatusForbidden: codes.PermissionDenied, + http.StatusNotFound: codes.NotFound, + http.StatusConflict: codes.Aborted, + http.StatusRequestedRangeNotSatisfiable: codes.OutOfRange, + http.StatusTooManyRequests: codes.ResourceExhausted, + http.StatusGatewayTimeout: codes.DeadlineExceeded, + http.StatusNotImplemented: codes.Unimplemented, + http.StatusServiceUnavailable: codes.Unavailable, + http.StatusUnauthorized: codes.Unauthenticated, +} + +// toCode maps an http code to the most correct equivalent. +func toCode(httpCode int) codes.Code { + if sCode, ok := canonicalMap[httpCode]; ok { + return sCode + } + switch { + case httpCode >= 200 && httpCode < 300: + return codes.OK + + case httpCode >= 400 && httpCode < 500: + return codes.FailedPrecondition + + case httpCode >= 500 && httpCode < 600: + return codes.Internal + } + return codes.Unknown +} + // ErrDetails holds the google/rpc/error_details.proto messages. type ErrDetails struct { ErrorInfo *errdetails.ErrorInfo @@ -217,6 +251,11 @@ func (a *APIError) Error() string { // GRPCStatus extracts the underlying gRPC Status error. // This method is necessary to fulfill the interface // described in https://pkg.go.dev/google.golang.org/grpc/status#FromError. +// +// For errors that originated as an HTTP-based googleapi.Error, GRPCStatus() +// returns a status that attempts to map from the original HTTP code to an +// equivalent gRPC status code. For use cases where you want to avoid this +// behavior, error unwrapping can be used. func (a *APIError) GRPCStatus() *status.Status { return a.status } @@ -243,9 +282,9 @@ func (a *APIError) Metadata() map[string]string { // setDetailsFromError parses a Status error or a googleapi.Error // and sets status and details or httpErr and details, respectively. // It returns false if neither Status nor googleapi.Error can be parsed. -// When err is a googleapi.Error, the status of the returned error will -// be set to an Unknown error, rather than nil, since a nil code is -// interpreted as OK in the gRPC status package. +// +// When err is a googleapi.Error, the status of the returned error will be +// mapped to the closest equivalent gGRPC status code. func (a *APIError) setDetailsFromError(err error) bool { st, isStatus := status.FromError(err) var herr *googleapi.Error @@ -258,7 +297,7 @@ func (a *APIError) setDetailsFromError(err error) bool { case isHTTPErr: a.httpErr = herr a.details = parseHTTPDetails(herr) - a.status = status.New(codes.Unknown, herr.Message) + a.status = status.New(toCode(a.httpErr.Code), herr.Message) default: return false } diff --git a/vendor/github.com/googleapis/gax-go/v2/internal/version.go b/vendor/github.com/googleapis/gax-go/v2/internal/version.go index e272d4d72..0ab1bce59 100644 --- a/vendor/github.com/googleapis/gax-go/v2/internal/version.go +++ b/vendor/github.com/googleapis/gax-go/v2/internal/version.go @@ -30,4 +30,4 @@ package internal // Version is the current tagged release of the library. -const Version = "2.14.2" +const Version = "2.15.0" diff --git a/vendor/google.golang.org/api/internal/gensupport/resumable.go b/vendor/google.golang.org/api/internal/gensupport/resumable.go index d74fe2a29..143a48923 100644 --- a/vendor/google.golang.org/api/internal/gensupport/resumable.go +++ b/vendor/google.golang.org/api/internal/gensupport/resumable.go @@ -126,16 +126,21 @@ func (rx *ResumableUpload) reportProgress(old, updated int64) { } } -// transferChunk performs a single HTTP request to upload a single chunk from rx.Media. -func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, error) { - chunk, off, size, err := rx.Media.Chunk() - - done := err == io.EOF - if !done && err != nil { - return nil, err +// transferChunk performs a single HTTP request to upload a single chunk. +func (rx *ResumableUpload) transferChunk(ctx context.Context, chunk io.Reader, off, size int64, done bool) (*http.Response, error) { + // rCtx is derived from a context with a defined ChunkTransferTimeout with non-zero value. + // If a particular request exceeds this transfer time for getting response, the rCtx deadline will be exceeded, + // triggering a retry of the request. + var rCtx context.Context + var cancel context.CancelFunc + + rCtx = ctx + if rx.ChunkTransferTimeout != 0 { + rCtx, cancel = context.WithTimeout(ctx, rx.ChunkTransferTimeout) + defer cancel() } - res, err := rx.doUploadRequest(ctx, chunk, off, int64(size), done) + res, err := rx.doUploadRequest(rCtx, chunk, off, size, done) if err != nil { return res, err } @@ -149,16 +154,98 @@ func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, e if res.StatusCode == http.StatusOK { rx.reportProgress(off, off+int64(size)) } + return res, nil +} - if statusResumeIncomplete(res) { - rx.Media.Next() +// 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. + retryDeadline := defaultRetryDeadline + if rx.ChunkRetryDeadline != 0 { + retryDeadline = rx.ChunkRetryDeadline + } + + // Each chunk gets its own initialized-at-zero backoff and invocation ID. + bo := rx.Retry.backoff() + quitAfterTimer := time.NewTimer(retryDeadline) + 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. + pauseTimer := time.NewTimer(pause) + select { + case <-ctx.Done(): + pauseTimer.Stop() + if err == nil { + err = ctx.Err() + } + return resp, err + case <-pauseTimer.C: + case <-quitAfterTimer.C: + pauseTimer.Stop() + return resp, err + } + pauseTimer.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 + } + // 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 } - return res, nil } // Upload starts the process of a resumable upload with a cancellable context. -// It retries using the provided back off strategy until cancelled or the -// strategy indicates to stop retrying. // 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). @@ -166,144 +253,47 @@ func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, e // 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) (resp *http.Response, err error) { +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 + } - // 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. - var prepareReturn = func(resp *http.Response, err error) (*http.Response, error) { + 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, 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 ChunkRetryDealine", rx.URI) + return nil, fmt.Errorf("upload request to %v not sent, choose larger value for ChunkRetryDeadline", rx.URI) } - return resp, nil - } - // Configure retryable error criteria. - errorFunc := rx.Retry.errorFunc() - - // Configure per-chunk retry deadline. - var retryDeadline time.Duration - if rx.ChunkRetryDeadline != 0 { - retryDeadline = rx.ChunkRetryDeadline - } else { - retryDeadline = defaultRetryDeadline - } - - // Send all chunks. - for { - var pause time.Duration - - // Each chunk gets its own initialized-at-zero backoff and invocation ID. - bo := rx.Retry.backoff() - quitAfterTimer := time.NewTimer(retryDeadline) - rx.attempts = 1 - rx.invocationID = uuid.New().String() - - // Retry loop for a single chunk. - for { - pauseTimer := time.NewTimer(pause) - select { - case <-ctx.Done(): - quitAfterTimer.Stop() - pauseTimer.Stop() - if err == nil { - err = ctx.Err() - } - return prepareReturn(resp, err) - case <-pauseTimer.C: - case <-quitAfterTimer.C: - pauseTimer.Stop() - return prepareReturn(resp, err) - } - pauseTimer.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(): - quitAfterTimer.Stop() - if err == nil { - err = ctx.Err() - } - return prepareReturn(resp, err) - case <-quitAfterTimer.C: - return prepareReturn(resp, err) - default: - } - // rCtx is derived from a context with a defined transferTimeout with non-zero value. - // If a particular request exceeds this transfer time for getting response, the rCtx deadline will be exceeded, - // triggering a retry of the request. - var rCtx context.Context - var cancel context.CancelFunc - - rCtx = ctx - if rx.ChunkTransferTimeout != 0 { - rCtx, cancel = context.WithTimeout(ctx, rx.ChunkTransferTimeout) - } - - // 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 + 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() } - resp, err = rx.transferChunk(rCtx) - - var status int - if resp != nil { - status = resp.StatusCode - } - - // The upload should be retried if the rCtx is canceled due to a timeout. - select { - case <-rCtx.Done(): - if rx.ChunkTransferTimeout != 0 && errors.Is(rCtx.Err(), context.DeadlineExceeded) { - // Cancel the context for rCtx - cancel() - continue - } - default: - } - - // Check if we should retry the request. - if !errorFunc(status, err) { - quitAfterTimer.Stop() - break - } - - rx.attempts++ - pause = bo.Pause() - } - - // If the chunk was uploaded successfully, but there's still - // more to go, upload the next chunk without any delay. - if statusResumeIncomplete(resp) { + rx.Media.Next() continue } - - return prepareReturn(resp, err) + return resp, nil } } diff --git a/vendor/google.golang.org/api/internal/version.go b/vendor/google.golang.org/api/internal/version.go index 7def1b70f..dfdef9bbb 100644 --- a/vendor/google.golang.org/api/internal/version.go +++ b/vendor/google.golang.org/api/internal/version.go @@ -5,4 +5,4 @@ package internal // Version is the current tagged release of the library. -const Version = "0.242.0" +const Version = "0.243.0" diff --git a/vendor/modules.txt b/vendor/modules.txt index 6f0d22aa6..8ef13533a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,7 +7,7 @@ cloud.google.com/go/internal cloud.google.com/go/internal/optional cloud.google.com/go/internal/trace cloud.google.com/go/internal/version -# cloud.google.com/go/auth v0.16.2 +# cloud.google.com/go/auth v0.16.3 ## explicit; go 1.23.0 cloud.google.com/go/auth cloud.google.com/go/auth/credentials @@ -766,7 +766,7 @@ github.com/google/uuid ## explicit; go 1.23.0 github.com/googleapis/enterprise-certificate-proxy/client github.com/googleapis/enterprise-certificate-proxy/client/util -# github.com/googleapis/gax-go/v2 v2.14.2 +# github.com/googleapis/gax-go/v2 v2.15.0 ## explicit; go 1.23.0 github.com/googleapis/gax-go/v2 github.com/googleapis/gax-go/v2/apierror @@ -1171,7 +1171,7 @@ golang.org/x/text/unicode/norm # golang.org/x/time v0.12.0 ## explicit; go 1.23.0 golang.org/x/time/rate -# google.golang.org/api v0.242.0 +# google.golang.org/api v0.243.0 ## explicit; go 1.23.0 google.golang.org/api/googleapi google.golang.org/api/googleapi/transport @@ -1188,13 +1188,13 @@ google.golang.org/api/storage/v1 google.golang.org/api/transport google.golang.org/api/transport/grpc google.golang.org/api/transport/http -# google.golang.org/genproto v0.0.0-20250519155744-55703ea1f237 +# google.golang.org/genproto v0.0.0-20250603155806-513f23925822 ## explicit; go 1.23.0 google.golang.org/genproto/googleapis/type/calendarperiod google.golang.org/genproto/googleapis/type/date google.golang.org/genproto/googleapis/type/expr google.golang.org/genproto/googleapis/type/timeofday -# google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 +# google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 ## explicit; go 1.23.0 google.golang.org/genproto/googleapis/api google.golang.org/genproto/googleapis/api/annotations @@ -1203,7 +1203,7 @@ google.golang.org/genproto/googleapis/api/expr/v1alpha1 google.golang.org/genproto/googleapis/api/label google.golang.org/genproto/googleapis/api/metric google.golang.org/genproto/googleapis/api/monitoredres -# google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 +# google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 ## explicit; go 1.23.0 google.golang.org/genproto/googleapis/rpc/code google.golang.org/genproto/googleapis/rpc/errdetails