build(deps): bump google.golang.org/grpc from 1.82.0 to 1.83.0

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.0 to 1.83.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.82.0...v1.83.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.83.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2026-08-03 13:26:55 +00:00
committed by Ralf Haferkamp
parent 557adfc327
commit 9df3881128
24 changed files with 265 additions and 2287 deletions

5
go.mod
View File

@@ -75,6 +75,7 @@ require (
github.com/rogpeppe/go-internal v1.15.0
github.com/rs/cors v1.11.1
github.com/rs/zerolog v1.35.1
github.com/shamaton/msgpack/v2 v2.4.1
github.com/sirupsen/logrus v1.9.4
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
@@ -84,7 +85,6 @@ require (
github.com/test-go/testify v1.1.4
github.com/testcontainers/testcontainers-go v0.43.0
github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0
github.com/theckman/yacspin v0.13.12
github.com/thejerf/suture/v4 v4.0.6
github.com/tidwall/gjson v1.19.0
github.com/tidwall/sjson v1.2.5
@@ -111,7 +111,7 @@ require (
golang.org/x/term v0.45.0
golang.org/x/text v0.40.0
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa
google.golang.org/grpc v1.82.0
google.golang.org/grpc v1.83.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@@ -344,7 +344,6 @@ require (
github.com/sergi/go-diff v1.4.0 // indirect
github.com/sethvargo/go-diceware v0.5.0 // indirect
github.com/sethvargo/go-password v0.3.1 // indirect
github.com/shamaton/msgpack/v2 v2.4.1 // indirect
github.com/shirou/gopsutil/v4 v4.26.5 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect

6
go.sum
View File

@@ -1191,8 +1191,6 @@ github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0 h1:a1ipjF
github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0/go.mod h1:OWSeUDiGMUy30iMsAltIJIo9uh/CleLv6KyxjYOsgR8=
github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o=
github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
github.com/theckman/yacspin v0.13.12 h1:CdZ57+n0U6JMuh2xqjnjRq5Haj6v1ner2djtLQRzJr4=
github.com/theckman/yacspin v0.13.12/go.mod h1:Rd2+oG2LmQi5f3zC3yeZAOl245z8QOvrH4OPOJNZxLg=
github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -1722,8 +1720,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=

View File

@@ -1,25 +0,0 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool
*.out
coverage.txt
# Dependency directories (remove the comment below to include it)
# vendor/
# README GIF generation files
*.mov
*.mp4
*.gif
*.prproj
# OS files
.DS_Store

View File

@@ -1,71 +0,0 @@
run:
tests: true
# all available settings of specific linters
linters-settings:
govet:
# report about shadowed variables
check-shadowing: true
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
dupl:
# tokens count to trigger issue, 150 by default
threshold: 100
goconst:
# minimal length of string constant, 3 by default
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 3
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
staticcheck:
checks: [ "all" ]
revive:
confidence: 0.8
ignore-generated-header: true
rules:
- name: context-keys-type
- name: time-naming
- name: var-declaration
- name: unexported-return
- name: errorf
- name: blank-imports
- name: context-as-argument
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: increment-decrement
- name: var-naming
- name: package-comments
- name: range
- name: receiver-naming
- name: indent-error-flow
- name: superfluous-else
- name: struct-tag
- name: modifies-value-receiver
- name: range-val-in-closure
- name: range-val-address
- name: atomic
- name: empty-lines
- name: early-return
- name: useless-break
linters:
enable:
- revive
- govet
- gosec
- staticcheck
- typecheck
fast: false
issues:
exclude-use-default: false
exclude:
- G104

View File

@@ -1,201 +0,0 @@
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

@@ -1,274 +0,0 @@
# Yet Another CLi Spinner (for Go)
[![License](https://img.shields.io/github/license/theckman/yacspin.svg)](https://github.com/theckman/yacspin/blob/master/LICENSE)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/theckman/yacspin)
[![Latest Git Tag](https://img.shields.io/github/tag/theckman/yacspin.svg)](https://github.com/theckman/yacspin/releases)
[![GitHub Actions master Build Status](https://github.com/theckman/yacspin/actions/workflows/tests.yaml/badge.svg?branch=master)](https://github.com/theckman/yacspin/actions/workflows/tests.yaml)
[![Go Report Card](https://goreportcard.com/badge/github.com/theckman/yacspin)](https://goreportcard.com/report/github.com/theckman/yacspin)
[![Codecov](https://img.shields.io/codecov/c/github/theckman/yacspin)](https://codecov.io/gh/theckman/yacspin)
Package `yacspin` provides yet another CLi spinner for Go, taking inspiration
(and some utility code) from the https://github.com/briandowns/spinner project.
Specifically `yacspin` borrows the default character sets, and color mappings to
github.com/fatih/color colors, from that project.
## License
Because this package adopts the spinner character sets from https://github.com/briandowns/spinner,
this package is released under the Apache 2.0 License.
## Yet Another CLi Spinner?
This project was created after it was realized that the most popular spinner
library for Go had some limitations, that couldn't be fixed without a massive
overhaul of the API.
The other spinner ties the ability to show updated messages to the spinner's
animation, meaning you can't always show all the information you want to the end
user without changing the animation speed. This means you need to trade off
animation aesthetics to show "realtime" information. It was a goal to avoid this
problem.
In addition, there were also some API design choices that have made it unsafe
for concurrent use, which presents challenges when trying to update the text in
the spinner while it's animating. This could result in undefined behavior due to
data races.
There were also some variable-width spinners in that other project that did
not render correctly. Because the width of the spinner animation would change,
so would the position of the message on the screen. `yacspin` uses a dynamic
width when animating, so your message should appear static relative to the
animating spinner.
Finally, there was an interest in the spinner being able to represent a task, and to
indicate whether it failed or was successful. This would have further compounded
the API changes needed above to support in an intuitive way.
This project takes inspiration from that other project, and takes a new approach
to address the challenges above.
## Features
#### Provided Spinners
There are over 90 spinners available in the `CharSets` package variable. They
were borrowed from [github.com/briandowns/spinner](https://github.com/briandowns/spinner).
There is a table with most of the spinners [at the bottom of this README](#Spinners).
#### Dynamic Width of Animation
Because of how some spinners are animated, they may have different widths are
different times in the animation. `yacspin` calculates the maximum width, and
pads the animation to ensure the text's position on the screen doesn't change.
This results in a smoother looking animation.
##### yacspin
![yacspin animation with dynamic width](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/width_good.gif)
##### other spinners
![other spinners' animation with dynamic width](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/width_bad.gif)
#### Success and Failure Results
The spinner has both a `Stop()` and `StopFail()` method, which allows the
spinner to result in a success message or a failure message. The messages,
colors, and even the character used to denote success or failure are
customizable in either the initial config or via the spinner's methods.
By doing this you can use a single `yacspin` spinner to display the status of a
list of tasks being executed serially.
##### Stop
![Animation with Success](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/stop.gif)
##### StopFail
![Animation with Failure](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/stop_fail.gif)
#### Animation At End of Line
The `SpinnerAtEnd` field of the `Config` struct allows you to specify whether
the spinner is rendered at the end of the line instead of the beginning. The
default value (`false`) results in the spinner being rendered at the beginning
of the line.
#### Concurrency
The spinner is safe for concurrent use, so you can update any of its settings
via methods whether the spinner is stopped or is currently animating.
#### Live Updates
Most spinners tie the ability to show new messages with the animation of the
spinner. So if the spinner animates every 200ms, you can only show updated
information every 200ms. If you wanted more frequent updates, you'd need to
tradeoff the asthetics of the animation to display more data.
This spinner updates the printed information of the spinner immediately on
change, without the animation updating. This allows you to use an animation
speed that looks astheticaly pleasing, while also knowing the data presented to
the user will be updated live.
You can see this in action in the following gif, where the filenames being
uploaded are rendered independent of the spinner being animated:
![Animation with Success](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/stop.gif)
#### Pausing for Updates
Sometimes you want to change a few settings, and don't want the spinner to
render your partially applied configuration. If your spinner is running, and you
want to change a few configuration items via method calls, you can `Pause()` the
spinner first. After making the changes you can call `Unpause()`, and it will
continue rendering like normal with the newly applied configuration.
#### Supporting Non-Interactive (TTY) Output Targets
`yacspin` also has native support for non-interactive (TTY) output targets. By
default this is detected in the constructor, or can be overriden via the
`TerminalMode` `Config` struct field. When detecting the application is not
running withn a TTY session, the behavior of the spinner is different.
Specifically, when this is automatically detected the spinner no longer uses
colors, disables the automatic spinner animation, and instead only animates the
spinner when updating the message. In addition, each animation is rendered on a
new line instead of overwriting the current line.
This should result in human-readable output without any changes needed by
consumers, even when the system is writing to a non-TTY destination.
#### Manually Stepping Animation
If you'd like to manually animate the spinner, you can do so by setting the
`TerminalMode` to `ForceNoTTYMode | ForceSmartTerminalMode`. In this mode the
spinner will still use colors and other text stylings, but the animation only
happens when data is updated and on individual lines. You can accomplish this by
calling the `Message()` method with the same used previously.
## Usage
```
go get github.com/theckman/yacspin
```
Within the `yacspin` package there are some default spinners stored in the
`yacspin.CharSets` variable, and you can also provide your own. There is also a
list of known colors in the `yacspin.ValidColors` variable.
### Example
There are runnable examples in the [examples/](https://github.com/theckman/yacspin/tree/master/examples)
directory, with one simple example and one more advanced one. Here is a quick
snippet showing usage from a very high level, with error handling omitted:
```Go
cfg := yacspin.Config{
Frequency: 100 * time.Millisecond,
CharSet: yacspin.CharSets[59],
Suffix: " backing up database to S3",
SuffixAutoColon: true,
Message: "exporting data",
StopCharacter: "✓",
StopColors: []string{"fgGreen"},
}
spinner, err := yacspin.New(cfg)
// handle the error
err = spinner.Start()
// doing some work
time.Sleep(2 * time.Second)
spinner.Message("uploading data")
// upload...
time.Sleep(2 * time.Second)
err = spinner.Stop()
```
## Spinners
The spinner animations below are recorded at a refresh frequency of 200ms. Some
animations may look better at a different speed, so play around with the
frequency until you find a value you find aesthetically pleasing.
yacspin.CharSets index | sample gif (Frequency: 200ms)
-----------------------|------------------------------
0 | ![0 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/0.gif)
1 | ![1 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/1.gif)
2 | ![2 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/2.gif)
3 | ![3 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/3.gif)
4 | ![4 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/4.gif)
5 | ![5 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/5.gif)
6 | ![6 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/6.gif)
7 | ![7 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/7.gif)
8 | ![8 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/8.gif)
9 | ![9 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/9.gif)
10 | ![10 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/10.gif)
11 | ![11 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/11.gif)
12 | ![12 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/12.gif)
13 | ![13 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/13.gif)
14 | ![14 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/14.gif)
15 | ![15 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/15.gif)
16 | ![16 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/16.gif)
17 | ![17 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/17.gif)
18 | ![18 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/18.gif)
19 | ![19 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/19.gif)
20 | ![20 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/20.gif)
21 | ![21 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/21.gif)
22 | ![22 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/22.gif)
23 | ![23 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/23.gif)
24 | ![24 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/24.gif)
25 | ![25 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/25.gif)
26 | ![26 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/26.gif)
27 | ![27 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/27.gif)
28 | ![28 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/28.gif)
29 | ![29 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/29.gif)
30 | ![30 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/30.gif)
31 | ![31 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/31.gif)
32 | ![32 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/32.gif)
33 | ![33 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/33.gif)
34 | ![34 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/34.gif)
35 | ![35 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/35.gif)
36 | ![36 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/36.gif)
37 | ![37 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/37.gif)
38 | ![38 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/38.gif)
39 | ![39 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/39.gif)
40 | ![40 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/40.gif)
41 | ![41 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/41.gif)
42 | ![42 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/42.gif)
43 | ![43 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/43.gif)
44 | ![44 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/44.gif)
45 | ![45 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/45.gif)
46 | ![46 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/46.gif)
47 | ![47 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/47.gif)
48 | ![48 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/48.gif)
49 | ![49 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/49.gif)
50 | ![50 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/50.gif)
51 | ![51 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/51.gif)
52 | ![52 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/52.gif)
53 | ![53 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/53.gif)
54 | ![54 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/54.gif)
55 | ![55 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/55.gif)
56 | ![56 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/56.gif)
57 | ![57 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/57.gif)
58 | ![58 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/58.gif)
59 | ![59 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/59.gif)
60 | ![60 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/60.gif)
61 | ![61 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/61.gif)
62 | ![62 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/62.gif)
63 | ![63 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/63.gif)
64 | ![64 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/64.gif)
65 | ![65 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/65.gif)
66 | ![66 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/66.gif)
67 | ![67 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/67.gif)
68 | ![68 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/68.gif)
69 | ![69 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/69.gif)
70 | ![70 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/70.gif)
71 | ![71 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/71.gif)
72 | ![72 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/72.gif)
73 | ![73 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/73.gif)
74 | ![74 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/74.gif)
75 | ![75 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/75.gif)
76 | ![76 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/76.gif)
77 | ![77 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/77.gif)
78 | ![78 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/78.gif)
79 | ![79 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/79.gif)
80 | ![80 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/80.gif)
81 | ![81 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/81.gif)
82 | ![82 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/82.gif)
83 | ![83 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/83.gif)
84 | ![84 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/84.gif)
85 | ![85 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/85.gif)
86 | ![86 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/86.gif)
87 | ![87 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/87.gif)
88 | ![88 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/88.gif)
89 | ![89 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/89.gif)
90 | ![90 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/90.gif)

View File

@@ -1,121 +0,0 @@
// Copyright (c) 2021 Brian J. Downs
// Copyright (c) 2019-2021 Tim Heckman
//
// 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.
//
// Please see the LICENSE file for the copy of the Apache 2.0 License.
//
// This file was copied from: https://github.com/briandowns/spinner
//
// Modifications:
//
// - removed runtime generation of CharSets 37 and 38; made them literals
// - fixed pipe spinner (32) animation, by adding missing frame
package yacspin
// CharSets contains the default character sets from
// https://github.com/briandowns/spinner.
var CharSets = map[int][]string{
0: {"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
1: {"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
2: {"▖", "▘", "▝", "▗"},
3: {"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
4: {"◢", "◣", "◤", "◥"},
5: {"◰", "◳", "◲", "◱"},
6: {"◴", "◷", "◶", "◵"},
7: {"◐", "◓", "◑", "◒"},
8: {".", "o", "O", "@", "*"},
9: {"|", "/", "-", "\\"},
10: {"◡◡", "⊙⊙", "◠◠"},
11: {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
12: {">))'>", " >))'>", " >))'>", " >))'>", " >))'>", " <'((<", " <'((<", " <'((<"},
13: {"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
14: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
15: {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
16: {"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
17: {"■", "□", "▪", "▫"},
18: {"←", "↑", "→", "↓"},
19: {"╫", "╪"},
20: {"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
21: {"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
22: {"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
23: {"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
24: {"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
25: {"ヲ", "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ン"},
26: {".", "..", "..."},
27: {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
28: {".", "o", "O", "°", "O", "o", "."},
29: {"+", "x"},
30: {"v", "<", "^", ">"},
31: {">>--->", " >>--->", " >>--->", " >>--->", " >>--->", " <---<<", " <---<<", " <---<<", " <---<<", "<---<<"},
32: {"|", "||", "|||", "||||", "|||||", "||||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
33: {"[ ]", "[= ]", "[== ]", "[=== ]", "[==== ]", "[===== ]", "[====== ]", "[======= ]", "[======== ]", "[========= ]", "[==========]"},
34: {"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
35: {"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
36: {"[ ]", "[=> ]", "[===> ]", "[=====> ]", "[======> ]", "[========> ]", "[==========> ]", "[============> ]", "[==============> ]", "[================> ]", "[==================> ]", "[===================>]"},
37: {"🕐", "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕚", "🕛"}, // clock emoji: one per hour for hours 1~12
38: {"🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡", "🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦", "🕛", "🕧"}, // clock emoji: one per half hour for hours 1~12
39: {"🌍", "🌎", "🌏"},
40: {"◜", "◝", "◞", "◟"},
41: {"⬒", "⬔", "⬓", "⬕"},
42: {"⬖", "⬘", "⬗", "⬙"},
43: {"[>>> >]", "[]>>>> []", "[] >>>> []", "[] >>>> []", "[] >>>> []", "[] >>>>[]", "[>> >>]"},
44: {"♠", "♣", "♥", "♦"},
45: {"➞", "➟", "➠", "➡", "➠", "➟"},
46: {" | ", ` \ `, "_ ", ` \ `, " | ", " / ", " _", " / "},
47: {" . . . .", ". . . .", ". . . .", ". . . .", ". . . . ", ". . . . ."},
48: {" | ", " / ", " _ ", ` \ `, " | ", ` \ `, " _ ", " / "},
49: {"⎺", "⎻", "⎼", "⎽", "⎼", "⎻"},
50: {"▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"},
51: {"[ ]", "[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]"},
52: {"( ● )", "( ● )", "( ● )", "( ● )", "( ●)", "( ● )", "( ● )", "( ● )", "( ● )"},
53: {"✶", "✸", "✹", "✺", "✹", "✷"},
54: {"▐|\\____________▌", "▐_|\\___________▌", "▐__|\\__________▌", "▐___|\\_________▌", "▐____|\\________▌", "▐_____|\\_______▌", "▐______|\\______▌", "▐_______|\\_____▌", "▐________|\\____▌", "▐_________|\\___▌", "▐__________|\\__▌", "▐___________|\\_▌", "▐____________|\\▌", "▐____________/|▌", "▐___________/|_▌", "▐__________/|__▌", "▐_________/|___▌", "▐________/|____▌", "▐_______/|_____▌", "▐______/|______▌", "▐_____/|_______▌", "▐____/|________▌", "▐___/|_________▌", "▐__/|__________▌", "▐_/|___________▌", "▐/|____________▌"},
55: {"▐⠂ ▌", "▐⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂▌", "▐ ⠠▌", "▐ ⡀▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐⠠ ▌"},
56: {"¿", "?"},
57: {"⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏"},
58: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"},
59: {". ", ".. ", "...", " ..", " .", " "},
60: {".", "o", "O", "°", "O", "o", "."},
61: {"▓", "▒", "░"},
62: {"▌", "▀", "▐", "▄"},
63: {"⊶", "⊷"},
64: {"▪", "▫"},
65: {"□", "■"},
66: {"▮", "▯"},
67: {"-", "=", "≡"},
68: {"d", "q", "p", "b"},
69: {"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"},
70: {"🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "},
71: {"☗", "☖"},
72: {"⧇", "⧆"},
73: {"◉", "◎"},
74: {"㊂", "㊀", "㊁"},
75: {"⦾", "⦿"},
76: {"", ""},
77: {"▌", "▀", "▐▄"},
78: {"⠈⠁", "⠈⠑", "⠈⠱", "⠈⡱", "⢀⡱", "⢄⡱", "⢄⡱", "⢆⡱", "⢎⡱", "⢎⡰", "⢎⡠", "⢎⡀", "⢎⠁", "⠎⠁", "⠊⠁"},
79: {"________", "-_______", "_-______", "__-_____", "___-____", "____-___", "_____-__", "______-_", "_______-", "________", "_______-", "______-_", "_____-__", "____-___", "___-____", "__-_____", "_-______", "-_______", "________"},
80: {"|_______", "_/______", "__-_____", "___\\____", "____|___", "_____/__", "______-_", "_______\\", "_______|", "______\\_", "_____-__", "____/___", "___|____", "__\\_____", "_-______"},
81: {"□", "◱", "◧", "▣", "■"},
82: {"□", "◱", "▨", "▩", "■"},
83: {"░", "▒", "▓", "█"},
84: {"░", "█"},
85: {"⚪", "⚫"},
86: {"◯", "⬤"},
87: {"▱", "▰"},
88: {"➊", "➋", "➌", "➍", "➎", "➏", "➐", "➑", "➒", "➓"},
89: {"½", "⅓", "⅔", "¼", "¾", "⅛", "⅜", "⅝", "⅞"},
90: {"↞", "↟", "↠", "↡"},
}

View File

@@ -1,174 +0,0 @@
// This file is available under the Apache 2.0 License
// This file was copied from: https://github.com/briandowns/spinner
//
// Please see the LICENSE file for the copy of the Apache 2.0 License.
//
// Modifications:
//
// - made validColors set map more idiomatic with an empty struct value
// - added a function for creating color functions from color list
package yacspin
import (
"fmt"
"github.com/fatih/color"
)
// ValidColors holds the list of the strings that are mapped to
// github.com/fatih/color color attributes. Any of these colors / attributes can
// be used with the *Spinner type, and it should be reflected in the output.
var ValidColors = map[string]struct{}{
// default colors for backwards compatibility
"black": {},
"red": {},
"green": {},
"yellow": {},
"blue": {},
"magenta": {},
"cyan": {},
"white": {},
// attributes
"reset": {},
"bold": {},
"faint": {},
"italic": {},
"underline": {},
"blinkslow": {},
"blinkrapid": {},
"reversevideo": {},
"concealed": {},
"crossedout": {},
// foreground text
"fgBlack": {},
"fgRed": {},
"fgGreen": {},
"fgYellow": {},
"fgBlue": {},
"fgMagenta": {},
"fgCyan": {},
"fgWhite": {},
// foreground Hi-Intensity text
"fgHiBlack": {},
"fgHiRed": {},
"fgHiGreen": {},
"fgHiYellow": {},
"fgHiBlue": {},
"fgHiMagenta": {},
"fgHiCyan": {},
"fgHiWhite": {},
// background text
"bgBlack": {},
"bgRed": {},
"bgGreen": {},
"bgYellow": {},
"bgBlue": {},
"bgMagenta": {},
"bgCyan": {},
"bgWhite": {},
// background Hi-Intensity text
"bgHiBlack": {},
"bgHiRed": {},
"bgHiGreen": {},
"bgHiYellow": {},
"bgHiBlue": {},
"bgHiMagenta": {},
"bgHiCyan": {},
"bgHiWhite": {},
}
// returns a valid color's foreground text color attribute
var colorAttributeMap = map[string]color.Attribute{
// default colors for backwards compatibility
"black": color.FgBlack,
"red": color.FgRed,
"green": color.FgGreen,
"yellow": color.FgYellow,
"blue": color.FgBlue,
"magenta": color.FgMagenta,
"cyan": color.FgCyan,
"white": color.FgWhite,
// attributes
"reset": color.Reset,
"bold": color.Bold,
"faint": color.Faint,
"italic": color.Italic,
"underline": color.Underline,
"blinkslow": color.BlinkSlow,
"blinkrapid": color.BlinkRapid,
"reversevideo": color.ReverseVideo,
"concealed": color.Concealed,
"crossedout": color.CrossedOut,
// foreground text colors
"fgBlack": color.FgBlack,
"fgRed": color.FgRed,
"fgGreen": color.FgGreen,
"fgYellow": color.FgYellow,
"fgBlue": color.FgBlue,
"fgMagenta": color.FgMagenta,
"fgCyan": color.FgCyan,
"fgWhite": color.FgWhite,
// foreground Hi-Intensity text colors
"fgHiBlack": color.FgHiBlack,
"fgHiRed": color.FgHiRed,
"fgHiGreen": color.FgHiGreen,
"fgHiYellow": color.FgHiYellow,
"fgHiBlue": color.FgHiBlue,
"fgHiMagenta": color.FgHiMagenta,
"fgHiCyan": color.FgHiCyan,
"fgHiWhite": color.FgHiWhite,
// background text colors
"bgBlack": color.BgBlack,
"bgRed": color.BgRed,
"bgGreen": color.BgGreen,
"bgYellow": color.BgYellow,
"bgBlue": color.BgBlue,
"bgMagenta": color.BgMagenta,
"bgCyan": color.BgCyan,
"bgWhite": color.BgWhite,
// background Hi-Intensity text colors
"bgHiBlack": color.BgHiBlack,
"bgHiRed": color.BgHiRed,
"bgHiGreen": color.BgHiGreen,
"bgHiYellow": color.BgHiYellow,
"bgHiBlue": color.BgHiBlue,
"bgHiMagenta": color.BgHiMagenta,
"bgHiCyan": color.BgHiCyan,
"bgHiWhite": color.BgHiWhite,
}
// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
_, ok := ValidColors[c]
return ok
}
func colorFunc(colors ...string) (func(format string, a ...interface{}) string, error) {
if len(colors) == 0 {
return fmt.Sprintf, nil
}
attrib := make([]color.Attribute, len(colors))
for i, color := range colors {
if !validColor(color) {
return nil, fmt.Errorf("%s is not a valid color", color)
}
attrib[i] = colorAttributeMap[color]
}
return color.New(attrib...).SprintfFunc(), nil
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -24,12 +24,10 @@ import (
"fmt"
"math"
"net/url"
"os"
"slices"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"google.golang.org/grpc/balancer"
@@ -1573,26 +1571,13 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address,
// to the provided transport.GoAwayInfo, as specified by gRFC A94:
// https://github.com/grpc/proposal/blob/master/A94-grpc-subchannel-disconnections-metrics.md
func disconnectErrorString(info transport.GoAwayInfo) string {
err := info.Err
var sysErr syscall.Errno
switch {
case info.Reason != transport.GoAwayInvalid:
if info.Reason != transport.GoAwayInvalid {
return fmt.Sprintf("GOAWAY %s", info.GoAwayCode.String())
case err == nil:
return "unknown"
case errors.Is(err, context.Canceled):
return "subchannel shutdown"
case errors.Is(err, syscall.ECONNRESET):
return "connection reset"
case errors.Is(err, syscall.ETIMEDOUT), errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded):
return "connection timed out"
case errors.Is(err, syscall.ECONNABORTED):
return "connection aborted"
case errors.As(err, &sysErr):
return "socket error"
default:
}
if info.Err == nil {
return "unknown"
}
return disconnectErrorLabel(info.Err)
}
// startHealthCheck starts the health checking stream (RPC) to watch the health

View File

@@ -0,0 +1,48 @@
//go:build !plan9
/*
*
* Copyright 2026 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"
"errors"
"os"
"syscall"
)
// disconnectErrorLabel returns the grpc.disconnect_error metric label for a
// transport error, as specified by gRFC A94.
func disconnectErrorLabel(err error) string {
var sysErr syscall.Errno
switch {
case errors.Is(err, context.Canceled):
return "subchannel shutdown"
case errors.Is(err, syscall.ECONNRESET):
return "connection reset"
case errors.Is(err, syscall.ETIMEDOUT), errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded):
return "connection timed out"
case errors.Is(err, syscall.ECONNABORTED):
return "connection aborted"
case errors.As(err, &sysErr):
return "socket error"
default:
return "unknown"
}
}

View File

@@ -0,0 +1,39 @@
/*
*
* Copyright 2026 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"
"errors"
"os"
)
// disconnectErrorLabel returns the grpc.disconnect_error metric label for a
// transport error, as specified by gRFC A94. syscall.Errno does not exist on
// plan9, so only the portable classifications are available.
func disconnectErrorLabel(err error) string {
switch {
case errors.Is(err, context.Canceled):
return "subchannel shutdown"
case errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded):
return "connection timed out"
default:
return "unknown"
}
}

View File

@@ -141,6 +141,17 @@ var (
// feature if unforeseen issues arise, and it will be removed in a future
// release.
EnableHTTPFramerReadBufferPooling = boolFromEnv("GRPC_GO_EXPERIMENTAL_HTTP_FRAMER_READ_BUFFER_POOLING", true)
// ControlBufferThrottleLimit is the maximum number of control frames that can
// be queued in the control buffer before throttling is applied. The value
// must be between 1 and 10,000, and is set to 100 by default.
//
// This environment variable serves as an escape hatch to increase the
// throttling limit if unforeseen issues arise, and it will be removed in a
// future release.
//
// TODO: Remove this env var once v1.83.0 is release.
ControlBufferThrottleLimit = uint64FromEnv("GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT", 100, 1, 10000)
)
func boolFromEnv(envVar string, def bool) bool {

View File

@@ -69,9 +69,8 @@ var (
// https://github.com/grpc/proposal/blob/master/A87-mtls-spiffe-support.md
XDSSPIFFEEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_MTLS_SPIFFE", false)
// XDSHTTPConnectEnabled is true if gRPC should parse custom Metadata
// configuring use of an HTTP CONNECT proxy via xDS from cluster resources.
// For more details, see:
// XDSHTTPConnectEnabled controls support for dynamic HTTP CONNECT proxying
// configured via the xDS control plane. For more details, see:
// https://github.com/grpc/proposal/blob/master/A86-xds-http-connect.md
XDSHTTPConnectEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT", false)
@@ -88,7 +87,7 @@ var (
// XDSORCAToLRSPropEnabled controls whether ORCA metrics are explicitly
// filtered and prefix-propagated to the LRS server. For more details, see:
// https://github.com/grpc/proposal/blob/master/A85-lrs-custom-metrics-changes.md
XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", false)
XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", true)
// XDSClientExtProcEnabled indicates whether ExtProc filter is enabled on
// the client side. For more details, see:

View File

@@ -20,10 +20,15 @@ package grpcsync
import (
"context"
"errors"
"google.golang.org/grpc/internal/buffer"
)
// ErrSerializerClosed is returned by ScheduleAndWait if the CallbackSerializer
// was closed before the callback could be scheduled.
var ErrSerializerClosed = errors.New("callback serializer is closed")
// CallbackSerializer provides a mechanism to schedule callbacks in a
// synchronized manner. It provides a FIFO guarantee on the order of execution
// of scheduled callbacks. New callbacks can be scheduled by invoking the
@@ -77,6 +82,27 @@ func (cs *CallbackSerializer) ScheduleOr(f func(ctx context.Context), onFailure
}
}
// ScheduleAndWait schedules the provided callback function f to be executed in
// the order it was added and blocks until f has run. If the context passed to
// NewCallbackSerializer was canceled before this method is called, f is not run
// and ScheduleAndWait returns ErrSerializerClosed.
//
// Callbacks are expected to honor the context when performing any blocking
// operations, and should return early when the context is canceled.
func (cs *CallbackSerializer) ScheduleAndWait(f func(ctx context.Context)) error {
done := make(chan struct{})
var err error
cs.ScheduleOr(func(ctx context.Context) {
f(ctx)
close(done)
}, func() {
err = ErrSerializerClosed
close(done)
})
<-done
return err
}
func (cs *CallbackSerializer) run(ctx context.Context) {
defer close(cs.done)

View File

@@ -24,7 +24,6 @@ import (
"sync"
"google.golang.org/grpc/internal/serviceconfig"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver"
)
@@ -52,82 +51,7 @@ type RPCConfig struct {
Context context.Context
MethodConfig serviceconfig.MethodConfig // configuration to use for this RPC
OnCommitted func() // Called when the RPC has been committed (retries no longer possible)
Interceptor ClientInterceptor
}
// ClientStream is the same as grpc.ClientStream, but defined here for circular
// dependency reasons.
type ClientStream interface {
// Header returns the header metadata received from the server if there
// is any. It blocks if the metadata is not ready to read.
Header() (metadata.MD, error)
// Trailer returns the trailer metadata from the server, if there is any.
// It must only be called after stream.CloseAndRecv has returned, or
// stream.Recv has returned a non-nil error (including io.EOF).
Trailer() metadata.MD
// CloseSend closes the send direction of the stream. It closes the stream
// when non-nil error is met. It is also not safe to call CloseSend
// concurrently with SendMsg.
CloseSend() error
// Context returns the context for this stream.
//
// It should not be called until after Header or RecvMsg has returned. Once
// called, subsequent client-side retries are disabled.
Context() context.Context
// SendMsg is generally called by generated code. On error, SendMsg aborts
// the stream. If the error was generated by the client, the status is
// returned directly; otherwise, io.EOF is returned and the status of
// the stream may be discovered using RecvMsg.
//
// SendMsg blocks until:
// - There is sufficient flow control to schedule m with the transport, or
// - The stream is done, or
// - The stream breaks.
//
// SendMsg does not wait until the message is received by the server. An
// untimely stream closure may result in lost messages. To ensure delivery,
// users should ensure the RPC completed successfully using RecvMsg.
//
// It is safe to have a goroutine calling SendMsg and another goroutine
// calling RecvMsg on the same stream at the same time, but it is not safe
// to call SendMsg on the same stream in different goroutines. It is also
// not safe to call CloseSend concurrently with SendMsg.
SendMsg(m any) error
// RecvMsg blocks until it receives a message into m or the stream is
// done. It returns io.EOF when the stream completes successfully. On
// any other error, the stream is aborted and the error contains the RPC
// status.
//
// It is safe to have a goroutine calling SendMsg and another goroutine
// calling RecvMsg on the same stream at the same time, but it is not
// safe to call RecvMsg on the same stream in different goroutines.
RecvMsg(m any) error
}
// ClientInterceptor is an interceptor for gRPC client streams.
type ClientInterceptor interface {
// NewStream creates a ClientStream for an RPC.
//
// Implementations must delegate stream creation to the provided newStream
// function. To intercept or override stream behavior, implementations
// may wrap the ClientStream returned by the delegate.
//
// Note: RPCInfo.Context is currently unused and will be nil.
//
// The done function is invoked when the RPC has finished using its
// underlying connection or if a connection could not be assigned. Because
// interceptors operate at the application layer, RPC operations may
// continue on the ClientStream even after done has been called. The
// caller must ensure done is non-nil.
//
// To ensure RPC completion notifications propagate through the entire
// interceptor chain, implementations must ensure that the done function
// passed to the delegate newStream invokes the done function passed to
// NewStream.
NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error)
// Close closes the interceptor. Once called, no new calls to NewStream are
// accepted. Ongoing calls to NewStream are allowed to complete.
Close()
Interceptor any
}
// ServerInterceptor is an interceptor for incoming RPC's on gRPC server side.

View File

@@ -39,9 +39,8 @@ const nonGRPCDataMaxLen = 1024
type ClientStream struct {
Stream // Embed for common stream functionality.
ct *http2Client
done chan struct{} // closed at the end of stream to unblock writers.
doneFunc func() // invoked at the end of stream.
ct *http2Client
done chan struct{} // closed at the end of stream to unblock writers.
headerChan chan struct{} // closed to indicate the end of header metadata.
header metadata.MD // the received header metadata

View File

@@ -29,6 +29,7 @@ import (
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/mem"
)
@@ -96,61 +97,70 @@ func (il *itemList) isEmpty() bool {
return il.head == nil
}
// maxQueuedControlBufferItems is the maximum number of frames (other than
// HEADERS and DATA) that we will buffer before preventing new reads from
// occurring on the transport. These are control frames sent in response to
// client requests, or frames that result in work being scheduled, such as
// RST_STREAM due to bad headers or settings acks.
var maxQueuedControlBufferItems = int(envconfig.ControlBufferThrottleLimit)
type cbItem interface {
isThrottled() bool
}
// throttledItem represents every item in the controlBuffer to which the overall
// throttling limit applies, other than outgoing HEADERS and DATA frames.
type throttledItem struct{}
func (throttledItem) isThrottled() bool { return true }
// The following defines various control items which could flow through
// the control buffer of transport. They represent different aspects of
// control tasks, e.g., flow control, settings, streaming resetting, etc.
// maxQueuedTransportResponseFrames is the most queued "transport response"
// frames we will buffer before preventing new reads from occurring on the
// transport. These are control frames sent in response to client requests,
// such as RST_STREAM due to bad headers or settings acks.
const maxQueuedTransportResponseFrames = 50
type cbItem interface {
isTransportResponseFrame() bool
}
// registerStream is used to register an incoming stream with loopy writer.
type registerStream struct {
throttledItem
streamID uint32
wq *writeQuota
}
func (*registerStream) isTransportResponseFrame() bool { return false }
// headerFrame is also used to register stream on the client-side.
type headerFrame struct {
type clientHeaders struct {
streamID uint32
hf []hpack.HeaderField
endStream bool // Valid on server side.
initStream func(uint32) error // Used only on the client side.
initStream func(uint32) error
onWrite func()
wq *writeQuota // write quota for the stream created.
cleanup *cleanupStream // Valid on the server side.
onOrphaned func(error) // Valid on client-side
wq *writeQuota
onOrphaned func(error)
}
func (h *headerFrame) isTransportResponseFrame() bool {
return h.cleanup != nil && h.cleanup.rst // Results in a RST_STREAM
func (*clientHeaders) isThrottled() bool { return false }
type serverHeaders struct {
streamID uint32
hf []hpack.HeaderField
endStream bool
onWrite func()
cleanup *cleanupStream
}
func (h *serverHeaders) isThrottled() bool { return false }
type cleanupStream struct {
throttledItem
streamID uint32
rst bool
rstCode http2.ErrCode
onWrite func()
}
func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM
type earlyAbortStream struct {
throttledItem
streamID uint32
rst bool
hf []hpack.HeaderField // Pre-built header fields
}
func (*earlyAbortStream) isTransportResponseFrame() bool { return false }
type dataFrame struct {
streamID uint32
endStream bool
@@ -162,70 +172,60 @@ type dataFrame struct {
onEachWrite func()
}
func (*dataFrame) isTransportResponseFrame() bool { return false }
func (*dataFrame) isThrottled() bool { return false }
type incomingWindowUpdate struct {
throttledItem
streamID uint32
increment uint32
}
func (*incomingWindowUpdate) isTransportResponseFrame() bool { return false }
type outgoingWindowUpdate struct {
throttledItem
streamID uint32
increment uint32
}
func (*outgoingWindowUpdate) isTransportResponseFrame() bool {
return false // window updates are throttled by thresholds
}
type incomingSettings struct {
throttledItem
ss []http2.Setting
}
func (*incomingSettings) isTransportResponseFrame() bool { return true } // Results in a settings ACK
type outgoingSettings struct {
throttledItem
ss []http2.Setting
}
func (*outgoingSettings) isTransportResponseFrame() bool { return false }
type incomingGoAway struct {
throttledItem
}
func (*incomingGoAway) isTransportResponseFrame() bool { return false }
type goAway struct {
throttledItem
code http2.ErrCode
debugData []byte
headsUp bool
closeConn error // if set, loopyWriter will exit with this error
}
func (*goAway) isTransportResponseFrame() bool { return false }
type ping struct {
throttledItem
ack bool
data [8]byte
}
func (*ping) isTransportResponseFrame() bool { return true }
type outFlowControlSizeRequest struct {
throttledItem
resp chan uint32
}
func (*outFlowControlSizeRequest) isTransportResponseFrame() bool { return false }
// closeConnection is an instruction to tell the loopy writer to flush the
// framer and exit, which will cause the transport's connection to be closed
// (by the client or server). The transport itself will close after the reader
// encounters the EOF caused by the connection closure.
type closeConnection struct{}
func (closeConnection) isTransportResponseFrame() bool { return false }
type closeConnection struct {
throttledItem
}
type outStreamState int
@@ -379,9 +379,9 @@ func (c *controlBuffer) executeAndPut(f func() bool, it cbItem) (bool, error) {
c.consumerWaiting = false
}
c.list.enqueue(it)
if it.isTransportResponseFrame() {
if it.isThrottled() {
c.transportResponseFrames++
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
if c.transportResponseFrames == maxQueuedControlBufferItems {
// We are adding the frame that puts us over the threshold; create
// a throttling channel.
ch := make(chan struct{})
@@ -436,8 +436,8 @@ func (c *controlBuffer) getOnceLocked() (any, error) {
return nil, nil
}
h := c.list.dequeue().(cbItem)
if h.isTransportResponseFrame() {
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
if h.isThrottled() {
if c.transportResponseFrames == maxQueuedControlBufferItems {
// We are removing the frame that put us over the
// threshold; close and clear the throttling channel.
ch := c.trfChan.Swap(nil)
@@ -464,10 +464,8 @@ func (c *controlBuffer) finish() {
// is still not aware of these yet.
for head := c.list.dequeueAll(); head != nil; head = head.next {
switch v := head.it.(type) {
case *headerFrame:
if v.onOrphaned != nil { // It will be nil on the server-side.
v.onOrphaned(ErrConnClosing)
}
case *clientHeaders:
v.onOrphaned(ErrConnClosing)
case *dataFrame:
if !v.processing {
v.data.Free()
@@ -680,42 +678,38 @@ func (l *loopyWriter) registerStreamHandler(h *registerStream) {
l.estdStreams[h.streamID] = str
}
func (l *loopyWriter) headerHandler(h *headerFrame) error {
if l.side == serverSide {
str, ok := l.estdStreams[h.streamID]
if !ok {
if l.logger.V(logLevel) {
l.logger.Infof("Unrecognized streamID %d in loopyWriter", h.streamID)
}
return nil
func (l *loopyWriter) serverHeaderHandler(hdr *serverHeaders) error {
str, ok := l.estdStreams[hdr.streamID]
if !ok {
if l.logger.V(logLevel) {
l.logger.Infof("Unrecognized streamID %d in loopyWriter", hdr.streamID)
}
// Case 1.A: Server is responding back with headers.
if !h.endStream {
return l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite)
}
// else: Case 1.B: Server wants to close stream.
return nil
}
if str.state != empty { // either active or waiting on stream quota.
// add it str's list of items.
str.itl.enqueue(h)
return nil
}
if err := l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite); err != nil {
return err
}
return l.cleanupStreamHandler(h.cleanup)
// Case 1: Server is responding back with headers.
if !hdr.endStream {
return l.writeHeader(hdr.streamID, hdr.endStream, hdr.hf, hdr.onWrite)
}
// Case 2: Client wants to originate stream.
str := &outStream{
id: h.streamID,
state: empty,
itl: &itemList{},
wq: h.wq,
// Case 2: Server is closing the stream.
if str.state != empty { // either active or waiting on stream quota.
str.itl.enqueue(hdr)
return nil
}
return l.originateStream(str, h)
if err := l.writeHeader(hdr.streamID, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
return err
}
return l.cleanupStreamHandler(hdr.cleanup)
}
func (l *loopyWriter) originateStream(str *outStream, hdr *headerFrame) error {
func (l *loopyWriter) clientHeaderHandler(hdr *clientHeaders) error {
str := &outStream{
id: hdr.streamID,
state: empty,
itl: &itemList{},
wq: hdr.wq,
}
// l.draining is set when handling GoAway. In which case, we want to avoid
// creating new streams.
if l.draining {
@@ -726,7 +720,7 @@ func (l *loopyWriter) originateStream(str *outStream, hdr *headerFrame) error {
if err := hdr.initStream(str.id); err != nil {
return err
}
if err := l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
if err := l.writeHeader(str.id, false, hdr.hf, hdr.onWrite); err != nil {
return err
}
l.estdStreams[str.id] = str
@@ -882,8 +876,10 @@ func (l *loopyWriter) handle(i any) error {
return l.incomingSettingsHandler(i)
case *outgoingSettings:
return l.outgoingSettingsHandler(i)
case *headerFrame:
return l.headerHandler(i)
case *clientHeaders:
return l.clientHeaderHandler(i)
case *serverHeaders:
return l.serverHeaderHandler(i)
case *registerStream:
l.registerStreamHandler(i)
case *cleanupStream:
@@ -1022,7 +1018,7 @@ func (l *loopyWriter) processData() (bool, error) {
func (l *loopyWriter) updateStreamAfterWrite(str *outStream) error {
if str.itl.isEmpty() {
str.state = empty
} else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers.
} else if trailer, ok := str.itl.peek().(*serverHeaders); ok { // the next item is trailers.
if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil {
return err
}

View File

@@ -498,7 +498,6 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr, handler s
ct: t,
done: make(chan struct{}),
headerChan: make(chan struct{}),
doneFunc: callHdr.DoneFunc,
statsHandler: handler,
}
s.Stream.buf.init()
@@ -806,9 +805,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s
close(s.headerChan)
}
}
hdr := &headerFrame{
hf: headerFields,
endStream: false,
hdr := &clientHeaders{
hf: headerFields,
initStream: func(uint32) error {
t.mu.Lock()
// TODO: handle transport closure in loopy instead and remove this
@@ -999,9 +997,6 @@ func (t *http2Client) closeStream(s *ClientStream, err error, rst bool, rstCode
t.controlBuf.executeAndPut(addBackStreamQuota, cleanup)
// This will unblock write.
close(s.done)
if s.doneFunc != nil {
s.doneFunc()
}
}
// Close kicks off the shutdown process of the transport. This should be called
@@ -1251,7 +1246,10 @@ func (t *http2Client) handleData(f *parsedDataFrame) {
dataLen := f.data.Len()
if f.Header().Flags.Has(http2.FlagDataPadded) {
if w := s.fc.onRead(size - uint32(dataLen)); w > 0 {
t.controlBuf.put(&outgoingWindowUpdate{s.id, w})
t.controlBuf.put(&outgoingWindowUpdate{
streamID: s.id,
increment: w,
})
}
}
if dataLen > 0 {
@@ -1879,7 +1877,7 @@ func (t *http2Client) getOutFlowWindow() int64 {
resp := make(chan uint32, 1)
timer := time.NewTimer(time.Second)
defer timer.Stop()
t.controlBuf.put(&outFlowControlSizeRequest{resp})
t.controlBuf.put(&outFlowControlSizeRequest{resp: resp})
select {
case sz := <-resp:
return int64(sz)

View File

@@ -810,7 +810,10 @@ func (t *http2Server) handleData(f *parsedDataFrame) {
dataLen := f.data.Len()
if f.Header().Flags.Has(http2.FlagDataPadded) {
if w := s.fc.onRead(size - uint32(dataLen)); w > 0 {
t.controlBuf.put(&outgoingWindowUpdate{s.id, w})
t.controlBuf.put(&outgoingWindowUpdate{
streamID: s.id,
increment: w,
})
}
}
if dataLen > 0 {
@@ -1047,7 +1050,7 @@ func (t *http2Server) writeHeaderLocked(s *ServerStream) error {
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
}
headerFields = appendHeaderFieldsFromMD(headerFields, s.header)
hf := &headerFrame{
hf := &serverHeaders{
streamID: s.id,
hf: headerFields,
endStream: false,
@@ -1115,7 +1118,7 @@ func (t *http2Server) writeStatus(s *ServerStream, st *status.Status) error {
// Attach the trailer metadata.
headerFields = appendHeaderFieldsFromMD(headerFields, s.trailer)
trailingHeader := &headerFrame{
trailingHeader := &serverHeaders{
streamID: s.id,
hf: headerFields,
endStream: true,
@@ -1325,7 +1328,7 @@ func (t *http2Server) deleteStream(s *ServerStream, eosReceived bool) {
}
// finishStream closes the stream and puts the trailing headerFrame into controlbuf.
func (t *http2Server) finishStream(s *ServerStream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) {
func (t *http2Server) finishStream(s *ServerStream, rst bool, rstCode http2.ErrCode, hdr *serverHeaders, eosReceived bool) {
// In case stream sending and receiving are invoked in separate
// goroutines (e.g., bi-directional streaming), cancel needs to be
// called to interrupt the potential blocking on other goroutines.
@@ -1464,7 +1467,7 @@ func (t *http2Server) getOutFlowWindow() int64 {
resp := make(chan uint32, 1)
timer := time.NewTimer(time.Second)
defer timer.Stop()
t.controlBuf.put(&outFlowControlSizeRequest{resp})
t.controlBuf.put(&outFlowControlSizeRequest{resp: resp})
select {
case sz := <-resp:
return int64(sz)

View File

@@ -594,8 +594,6 @@ type CallHdr struct {
PreviousAttempts int // value of grpc-previous-rpc-attempts header to set
DoneFunc func() // called when the stream is finished
// Authority is used to explicitly override the `:authority` header.
//
// This value comes from one of two sources:

View File

@@ -201,6 +201,15 @@ func endOfClientStream(cc *ClientConn, err error, opts ...CallOption) {
}
}
// clientInterceptor is structurally identical to the ClientInterceptor defined
// in internal/xds/httpfilter/httpfilter.go. It is defined locally here so that
// we can type-assert the generic Interceptor field in iresolver.RPCConfig
// without introducing a dependency on xDS packages.
type clientInterceptor interface {
NewStream(ctx context.Context, ri iresolver.RPCInfo, newStream func(ctx context.Context, opts ...CallOption) (ClientStream, error), opts ...CallOption) (ClientStream, error)
Close()
}
func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
if channelz.IsOn() {
cc.incrCallsStarted()
@@ -244,8 +253,11 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
mc := &emptyMethodConfig
var onCommit func()
newStream := func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, done, nameResolutionDelayed, opts...)
newStream := func(ctx context.Context, filterOpts ...CallOption) (ClientStream, error) {
if filterOpts != nil {
opts = combine(opts, filterOpts)
}
return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, nameResolutionDelayed, opts...)
}
rpcInfo := iresolver.RPCInfo{Context: ctx, Method: method}
@@ -270,20 +282,24 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
if rpcConfig.Interceptor != nil {
rpcInfo.Context = nil
ns := newStream
newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
cs, err := rpcConfig.Interceptor.NewStream(ctx, rpcInfo, done, ns)
if err != nil {
return nil, toRPCErr(err)
if interceptor, ok := rpcConfig.Interceptor.(clientInterceptor); ok {
newStream = func(ctx context.Context, filterOpts ...CallOption) (ClientStream, error) {
cs, err := interceptor.NewStream(ctx, rpcInfo, ns, filterOpts...)
if err != nil {
return nil, toRPCErr(err)
}
return cs, nil
}
return cs, nil
} else {
return nil, status.Errorf(codes.Internal, "invalid client interceptor type %T", rpcConfig.Interceptor)
}
}
}
return newStream(ctx, func() {})
return newStream(ctx)
}
func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc *serviceconfig.MethodConfig, onCommit, doneFunc func(), nameResolutionDelayed bool, opts ...CallOption) (_ iresolver.ClientStream, err error) {
func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc *serviceconfig.MethodConfig, onCommit func(), nameResolutionDelayed bool, opts ...CallOption) (_ ClientStream, err error) {
callInfo := defaultCallInfo()
if mc.WaitForReady != nil {
callInfo.failFast = !*mc.WaitForReady
@@ -321,7 +337,6 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
Host: cc.authority,
Method: method,
ContentSubtype: callInfo.contentSubtype,
DoneFunc: doneFunc,
Authority: callInfo.authority,
}
if allowed := callInfo.acceptedResponseCompressors; len(allowed) > 0 {

View File

@@ -19,4 +19,4 @@
package grpc
// Version is the current grpc version.
const Version = "1.82.0"
const Version = "1.83.0"

5
vendor/modules.txt vendored
View File

@@ -2071,9 +2071,6 @@ github.com/testcontainers/testcontainers-go/wait
# github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0
## explicit; go 1.25.0
github.com/testcontainers/testcontainers-go/modules/opensearch
# github.com/theckman/yacspin v0.13.12
## explicit; go 1.17
github.com/theckman/yacspin
# github.com/thejerf/suture/v4 v4.0.6
## explicit; go 1.9
github.com/thejerf/suture/v4
@@ -2574,7 +2571,7 @@ google.golang.org/genproto/googleapis/api/httpbody
## explicit; go 1.25.0
google.golang.org/genproto/googleapis/rpc/errdetails
google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.82.0
# google.golang.org/grpc v1.83.0
## explicit; go 1.25.0
google.golang.org/grpc
google.golang.org/grpc/attributes