mirror of
https://github.com/fumiama/terasu-cloudflared.git
synced 2026-06-07 19:40:30 +08:00
TUN-9016: update go to 1.24
## Summary Update several moving parts of cloudflared build system: * use goboring 1.24.2 in cfsetup * update linter and fix lint issues * update packages namely **quic-go and net** * install script for macos * update docker files to use go 1.24.1 * remove usage of cloudflare-go * pin golang linter Closes TUN-9016
This commit is contained in:
29
vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md
generated
vendored
29
vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md
generated
vendored
@@ -1,3 +1,32 @@
|
||||
## Changed
|
||||
|
||||
- Defined a custom error, ErrUnexpectedSignatureAlgorithm, returned when a JWS
|
||||
header contains an unsupported signature algorithm.
|
||||
|
||||
# v4.0.4
|
||||
|
||||
## Fixed
|
||||
|
||||
- Reverted "Allow unmarshalling JSONWebKeySets with unsupported key types" as a
|
||||
breaking change. See #136 / #137.
|
||||
|
||||
# v4.0.3
|
||||
|
||||
## Changed
|
||||
|
||||
- Allow unmarshalling JSONWebKeySets with unsupported key types (#130)
|
||||
- Document that OpaqueKeyEncrypter can't be implemented (for now) (#129)
|
||||
- Dependency updates
|
||||
|
||||
# v4.0.2
|
||||
|
||||
## Changed
|
||||
|
||||
- Improved documentation of Verify() to note that JSONWebKeySet is a supported
|
||||
argument type (#104)
|
||||
- Defined exported error values for missing x5c header and unsupported elliptic
|
||||
curves error cases (#117)
|
||||
|
||||
# v4.0.1
|
||||
|
||||
## Fixed
|
||||
|
||||
6
vendor/github.com/go-jose/go-jose/v4/CONTRIBUTING.md
generated
vendored
6
vendor/github.com/go-jose/go-jose/v4/CONTRIBUTING.md
generated
vendored
@@ -7,9 +7,3 @@ When submitting code, please make every effort to follow existing conventions
|
||||
and style in order to keep the code as readable as possible. Please also make
|
||||
sure all tests pass by running `go test`, and format your code with `go fmt`.
|
||||
We also recommend using `golint` and `errcheck`.
|
||||
|
||||
Before your code can be accepted into the project you must also sign the
|
||||
Individual Contributor License Agreement. We use [cla-assistant.io][1] and you
|
||||
will be prompted to sign once a pull request is opened.
|
||||
|
||||
[1]: https://cla-assistant.io/
|
||||
|
||||
10
vendor/github.com/go-jose/go-jose/v4/README.md
generated
vendored
10
vendor/github.com/go-jose/go-jose/v4/README.md
generated
vendored
@@ -9,14 +9,6 @@ Package jose aims to provide an implementation of the Javascript Object Signing
|
||||
and Encryption set of standards. This includes support for JSON Web Encryption,
|
||||
JSON Web Signature, and JSON Web Token standards.
|
||||
|
||||
**Disclaimer**: This library contains encryption software that is subject to
|
||||
the U.S. Export Administration Regulations. You may not export, re-export,
|
||||
transfer or download this code or any part of it in violation of any United
|
||||
States law, directive or regulation. In particular this software may not be
|
||||
exported or re-exported in any form or on any media to Iran, North Sudan,
|
||||
Syria, Cuba, or North Korea, or to denied persons or entities mentioned on any
|
||||
US maintained blocked list.
|
||||
|
||||
## Overview
|
||||
|
||||
The implementation follows the
|
||||
@@ -109,6 +101,6 @@ allows attaching a key id.
|
||||
|
||||
Examples can be found in the Godoc
|
||||
reference for this package. The
|
||||
[`jose-util`](https://github.com/go-jose/go-jose/tree/v4/jose-util)
|
||||
[`jose-util`](https://github.com/go-jose/go-jose/tree/main/jose-util)
|
||||
subdirectory also contains a small command-line utility which might be useful
|
||||
as an example as well.
|
||||
|
||||
10
vendor/github.com/go-jose/go-jose/v4/crypter.go
generated
vendored
10
vendor/github.com/go-jose/go-jose/v4/crypter.go
generated
vendored
@@ -459,7 +459,10 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
|
||||
}
|
||||
|
||||
key := tryJWKS(decryptionKey, obj.Header)
|
||||
key, err := tryJWKS(decryptionKey, obj.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter, err := newDecrypter(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -529,7 +532,10 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
|
||||
}
|
||||
|
||||
key := tryJWKS(decryptionKey, obj.Header)
|
||||
key, err := tryJWKS(decryptionKey, obj.Header)
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, err
|
||||
}
|
||||
decrypter, err := newDecrypter(key)
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, err
|
||||
|
||||
5
vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
5
vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
@@ -288,10 +288,11 @@ func ParseEncryptedCompact(
|
||||
keyAlgorithms []KeyAlgorithm,
|
||||
contentEncryption []ContentEncryption,
|
||||
) (*JSONWebEncryption, error) {
|
||||
parts := strings.Split(input, ".")
|
||||
if len(parts) != 5 {
|
||||
// Five parts is four separators
|
||||
if strings.Count(input, ".") != 4 {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts")
|
||||
}
|
||||
parts := strings.SplitN(input, ".", 5)
|
||||
|
||||
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
|
||||
25
vendor/github.com/go-jose/go-jose/v4/jwk.go
generated
vendored
25
vendor/github.com/go-jose/go-jose/v4/jwk.go
generated
vendored
@@ -239,10 +239,10 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
keyPub = key
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("go-jose/go-jose: unknown curve %s'", raw.Crv)
|
||||
return fmt.Errorf("go-jose/go-jose: unknown curve %s'", raw.Crv)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("go-jose/go-jose: unknown json web key type '%s'", raw.Kty)
|
||||
return fmt.Errorf("go-jose/go-jose: unknown json web key type '%s'", raw.Kty)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -779,7 +779,13 @@ func (key rawJSONWebKey) symmetricKey() ([]byte, error) {
|
||||
return key.K.bytes(), nil
|
||||
}
|
||||
|
||||
func tryJWKS(key interface{}, headers ...Header) interface{} {
|
||||
var (
|
||||
// ErrJWKSKidNotFound is returned when a JWKS does not contain a JWK with a
|
||||
// key ID which matches one in the provided tokens headers.
|
||||
ErrJWKSKidNotFound = errors.New("go-jose/go-jose: JWK with matching kid not found in JWK Set")
|
||||
)
|
||||
|
||||
func tryJWKS(key interface{}, headers ...Header) (interface{}, error) {
|
||||
var jwks JSONWebKeySet
|
||||
|
||||
switch jwksType := key.(type) {
|
||||
@@ -788,9 +794,11 @@ func tryJWKS(key interface{}, headers ...Header) interface{} {
|
||||
case JSONWebKeySet:
|
||||
jwks = jwksType
|
||||
default:
|
||||
return key
|
||||
// If the specified key is not a JWKS, return as is.
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Determine the KID to search for from the headers.
|
||||
var kid string
|
||||
for _, header := range headers {
|
||||
if header.KeyID != "" {
|
||||
@@ -799,14 +807,17 @@ func tryJWKS(key interface{}, headers ...Header) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// If no KID is specified in the headers, reject.
|
||||
if kid == "" {
|
||||
return key
|
||||
return nil, ErrJWKSKidNotFound
|
||||
}
|
||||
|
||||
// Find the JWK with the matching KID. If no JWK with the specified KID is
|
||||
// found, reject.
|
||||
keys := jwks.Key(kid)
|
||||
if len(keys) == 0 {
|
||||
return key
|
||||
return nil, ErrJWKSKidNotFound
|
||||
}
|
||||
|
||||
return keys[0].Key
|
||||
return keys[0].Key, nil
|
||||
}
|
||||
|
||||
57
vendor/github.com/go-jose/go-jose/v4/jws.go
generated
vendored
57
vendor/github.com/go-jose/go-jose/v4/jws.go
generated
vendored
@@ -75,7 +75,14 @@ type Signature struct {
|
||||
original *rawSignatureInfo
|
||||
}
|
||||
|
||||
// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization.
|
||||
// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization. Validation fails if
|
||||
// the JWS is signed with an algorithm that isn't in the provided list of signature algorithms.
|
||||
// Applications should decide for themselves which signature algorithms are acceptable. If you're
|
||||
// not sure which signature algorithms your application might receive, consult the documentation of
|
||||
// the program which provides them or the protocol that you are implementing. You can also try
|
||||
// getting an example JWS and decoding it with a tool like https://jwt.io to see what its "alg"
|
||||
// header parameter indicates. The signature on the JWS does not get validated during parsing. Call
|
||||
// Verify() after parsing to validate the signature and obtain the payload.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7
|
||||
func ParseSigned(
|
||||
@@ -90,7 +97,14 @@ func ParseSigned(
|
||||
return parseSignedCompact(signature, nil, signatureAlgorithms)
|
||||
}
|
||||
|
||||
// ParseSignedCompact parses a message in JWS Compact Serialization.
|
||||
// ParseSignedCompact parses a message in JWS Compact Serialization. Validation fails if the JWS is
|
||||
// signed with an algorithm that isn't in the provided list of signature algorithms. Applications
|
||||
// should decide for themselves which signature algorithms are acceptable.If you're not sure which
|
||||
// signature algorithms your application might receive, consult the documentation of the program
|
||||
// which provides them or the protocol that you are implementing. You can also try getting an
|
||||
// example JWS and decoding it with a tool like https://jwt.io to see what its "alg" header
|
||||
// parameter indicates. The signature on the JWS does not get validated during parsing. Call
|
||||
// Verify() after parsing to validate the signature and obtain the payload.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7.1
|
||||
func ParseSignedCompact(
|
||||
@@ -101,6 +115,15 @@ func ParseSignedCompact(
|
||||
}
|
||||
|
||||
// ParseDetached parses a signed message in compact serialization format with detached payload.
|
||||
// Validation fails if the JWS is signed with an algorithm that isn't in the provided list of
|
||||
// signature algorithms. Applications should decide for themselves which signature algorithms are
|
||||
// acceptable. If you're not sure which signature algorithms your application might receive, consult
|
||||
// the documentation of the program which provides them or the protocol that you are implementing.
|
||||
// You can also try getting an example JWS and decoding it with a tool like https://jwt.io to see
|
||||
// what its "alg" header parameter indicates. The signature on the JWS does not get validated during
|
||||
// parsing. Call Verify() after parsing to validate the signature and obtain the payload.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#appendix-F
|
||||
func ParseDetached(
|
||||
signature string,
|
||||
payload []byte,
|
||||
@@ -181,6 +204,25 @@ func containsSignatureAlgorithm(haystack []SignatureAlgorithm, needle SignatureA
|
||||
return false
|
||||
}
|
||||
|
||||
// ErrUnexpectedSignatureAlgorithm is returned when the signature algorithm in
|
||||
// the JWS header does not match one of the expected algorithms.
|
||||
type ErrUnexpectedSignatureAlgorithm struct {
|
||||
// Got is the signature algorithm found in the JWS header.
|
||||
Got SignatureAlgorithm
|
||||
expected []SignatureAlgorithm
|
||||
}
|
||||
|
||||
func (e *ErrUnexpectedSignatureAlgorithm) Error() string {
|
||||
return fmt.Sprintf("unexpected signature algorithm %q; expected %q", e.Got, e.expected)
|
||||
}
|
||||
|
||||
func newErrUnexpectedSignatureAlgorithm(got SignatureAlgorithm, expected []SignatureAlgorithm) error {
|
||||
return &ErrUnexpectedSignatureAlgorithm{
|
||||
Got: got,
|
||||
expected: expected,
|
||||
}
|
||||
}
|
||||
|
||||
// sanitized produces a cleaned-up JWS object from the raw JSON.
|
||||
func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgorithm) (*JSONWebSignature, error) {
|
||||
if len(signatureAlgorithms) == 0 {
|
||||
@@ -236,8 +278,7 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo
|
||||
|
||||
alg := SignatureAlgorithm(signature.Header.Algorithm)
|
||||
if !containsSignatureAlgorithm(signatureAlgorithms, alg) {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q",
|
||||
alg, signatureAlgorithms)
|
||||
return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms)
|
||||
}
|
||||
|
||||
if signature.header != nil {
|
||||
@@ -285,8 +326,7 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo
|
||||
|
||||
alg := SignatureAlgorithm(obj.Signatures[i].Header.Algorithm)
|
||||
if !containsSignatureAlgorithm(signatureAlgorithms, alg) {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q",
|
||||
alg, signatureAlgorithms)
|
||||
return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms)
|
||||
}
|
||||
|
||||
if obj.Signatures[i].header != nil {
|
||||
@@ -327,10 +367,11 @@ func parseSignedCompact(
|
||||
payload []byte,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
parts := strings.Split(input, ".")
|
||||
if len(parts) != 3 {
|
||||
// Three parts is two separators
|
||||
if strings.Count(input, ".") != 2 {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts")
|
||||
}
|
||||
parts := strings.SplitN(input, ".", 3)
|
||||
|
||||
if parts[1] != "" && payload != nil {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: payload is not detached")
|
||||
|
||||
3
vendor/github.com/go-jose/go-jose/v4/opaque.go
generated
vendored
3
vendor/github.com/go-jose/go-jose/v4/opaque.go
generated
vendored
@@ -83,6 +83,9 @@ func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg Sig
|
||||
}
|
||||
|
||||
// OpaqueKeyEncrypter is an interface that supports encrypting keys with an opaque key.
|
||||
//
|
||||
// Note: this cannot currently be implemented outside this package because of its
|
||||
// unexported method.
|
||||
type OpaqueKeyEncrypter interface {
|
||||
// KeyID returns the kid
|
||||
KeyID() string
|
||||
|
||||
10
vendor/github.com/go-jose/go-jose/v4/shared.go
generated
vendored
10
vendor/github.com/go-jose/go-jose/v4/shared.go
generated
vendored
@@ -71,6 +71,12 @@ var (
|
||||
// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
|
||||
// nonce header parameter was included in an unprotected header object.
|
||||
ErrUnprotectedNonce = errors.New("go-jose/go-jose: Nonce parameter included in unprotected header")
|
||||
|
||||
// ErrMissingX5cHeader indicates that the JWT header is missing x5c headers.
|
||||
ErrMissingX5cHeader = errors.New("go-jose/go-jose: no x5c header present in message")
|
||||
|
||||
// ErrUnsupportedEllipticCurve indicates unsupported or unknown elliptic curve has been found.
|
||||
ErrUnsupportedEllipticCurve = errors.New("go-jose/go-jose: unsupported/unknown elliptic curve")
|
||||
)
|
||||
|
||||
// Key management algorithms
|
||||
@@ -199,7 +205,7 @@ type Header struct {
|
||||
// not be validated with the given verify options.
|
||||
func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) {
|
||||
if len(h.certificates) == 0 {
|
||||
return nil, errors.New("go-jose/go-jose: no x5c header present in message")
|
||||
return nil, ErrMissingX5cHeader
|
||||
}
|
||||
|
||||
leaf := h.certificates[0]
|
||||
@@ -501,7 +507,7 @@ func curveName(crv elliptic.Curve) (string, error) {
|
||||
case elliptic.P521():
|
||||
return "P-521", nil
|
||||
default:
|
||||
return "", fmt.Errorf("go-jose/go-jose: unsupported/unknown elliptic curve")
|
||||
return "", ErrUnsupportedEllipticCurve
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
vendor/github.com/go-jose/go-jose/v4/signing.go
generated
vendored
12
vendor/github.com/go-jose/go-jose/v4/signing.go
generated
vendored
@@ -358,6 +358,8 @@ func (ctx *genericSigner) Options() SignerOptions {
|
||||
// - *rsa.PublicKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - *JSONWebKeySet
|
||||
// - JSONWebKeySet
|
||||
// - []byte (an HMAC key)
|
||||
// - Any type that implements the OpaqueVerifier interface.
|
||||
//
|
||||
@@ -388,7 +390,10 @@ func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
|
||||
key := tryJWKS(verificationKey, obj.headers()...)
|
||||
key, err := tryJWKS(verificationKey, obj.headers()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verifier, err := newVerifier(key)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -453,7 +458,10 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
|
||||
key := tryJWKS(verificationKey, obj.headers()...)
|
||||
key, err := tryJWKS(verificationKey, obj.headers()...)
|
||||
if err != nil {
|
||||
return -1, Signature{}, err
|
||||
}
|
||||
verifier, err := newVerifier(key)
|
||||
if err != nil {
|
||||
return -1, Signature{}, err
|
||||
|
||||
13
vendor/github.com/go-jose/go-jose/v4/symmetric.go
generated
vendored
13
vendor/github.com/go-jose/go-jose/v4/symmetric.go
generated
vendored
@@ -21,6 +21,7 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/pbkdf2"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
@@ -30,8 +31,6 @@ import (
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
josecipher "github.com/go-jose/go-jose/v4/cipher"
|
||||
)
|
||||
|
||||
@@ -330,7 +329,10 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
|
||||
|
||||
// derive key
|
||||
keyLen, h := getPbkdf2Params(alg)
|
||||
key := pbkdf2.Key(ctx.key, salt, ctx.p2c, keyLen, h)
|
||||
key, err := pbkdf2.Key(h, string(ctx.key), salt, ctx.p2c, keyLen)
|
||||
if err != nil {
|
||||
return recipientInfo{}, nil
|
||||
}
|
||||
|
||||
// use AES cipher with derived key
|
||||
block, err := aes.NewCipher(key)
|
||||
@@ -432,7 +434,10 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||
|
||||
// derive key
|
||||
keyLen, h := getPbkdf2Params(alg)
|
||||
key := pbkdf2.Key(ctx.key, salt, p2c, keyLen, h)
|
||||
key, err := pbkdf2.Key(h, string(ctx.key), salt, p2c, keyLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// use AES cipher with derived key
|
||||
block, err := aes.NewCipher(key)
|
||||
|
||||
1
vendor/github.com/go-logr/logr/README.md
generated
vendored
1
vendor/github.com/go-logr/logr/README.md
generated
vendored
@@ -1,6 +1,7 @@
|
||||
# A minimal logging API for Go
|
||||
|
||||
[](https://pkg.go.dev/github.com/go-logr/logr)
|
||||
[](https://goreportcard.com/report/github.com/go-logr/logr)
|
||||
[](https://securityscorecards.dev/viewer/?platform=github.com&org=go-logr&repo=logr)
|
||||
|
||||
logr offers an(other) opinion on how Go programs and libraries can do logging
|
||||
|
||||
185
vendor/github.com/go-logr/logr/funcr/funcr.go
generated
vendored
185
vendor/github.com/go-logr/logr/funcr/funcr.go
generated
vendored
@@ -236,15 +236,14 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
|
||||
// implementation. It should be constructed with NewFormatter. Some of
|
||||
// its methods directly implement logr.LogSink.
|
||||
type Formatter struct {
|
||||
outputFormat outputFormat
|
||||
prefix string
|
||||
values []any
|
||||
valuesStr string
|
||||
parentValuesStr string
|
||||
depth int
|
||||
opts *Options
|
||||
group string // for slog groups
|
||||
groupDepth int
|
||||
outputFormat outputFormat
|
||||
prefix string
|
||||
values []any
|
||||
valuesStr string
|
||||
depth int
|
||||
opts *Options
|
||||
groupName string // for slog groups
|
||||
groups []groupDef
|
||||
}
|
||||
|
||||
// outputFormat indicates which outputFormat to use.
|
||||
@@ -257,6 +256,13 @@ const (
|
||||
outputJSON
|
||||
)
|
||||
|
||||
// groupDef represents a saved group. The values may be empty, but we don't
|
||||
// know if we need to render the group until the final record is rendered.
|
||||
type groupDef struct {
|
||||
name string
|
||||
values string
|
||||
}
|
||||
|
||||
// PseudoStruct is a list of key-value pairs that gets logged as a struct.
|
||||
type PseudoStruct []any
|
||||
|
||||
@@ -264,76 +270,102 @@ type PseudoStruct []any
|
||||
func (f Formatter) render(builtins, args []any) string {
|
||||
// Empirically bytes.Buffer is faster than strings.Builder for this.
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte('{') // for the whole line
|
||||
buf.WriteByte('{') // for the whole record
|
||||
}
|
||||
|
||||
// Render builtins
|
||||
vals := builtins
|
||||
if hook := f.opts.RenderBuiltinsHook; hook != nil {
|
||||
vals = hook(f.sanitize(vals))
|
||||
}
|
||||
f.flatten(buf, vals, false, false) // keys are ours, no need to escape
|
||||
f.flatten(buf, vals, false) // keys are ours, no need to escape
|
||||
continuing := len(builtins) > 0
|
||||
|
||||
if f.parentValuesStr != "" {
|
||||
// Turn the inner-most group into a string
|
||||
argsStr := func() string {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
|
||||
vals = args
|
||||
if hook := f.opts.RenderArgsHook; hook != nil {
|
||||
vals = hook(f.sanitize(vals))
|
||||
}
|
||||
f.flatten(buf, vals, true) // escape user-provided keys
|
||||
|
||||
return buf.String()
|
||||
}()
|
||||
|
||||
// Render the stack of groups from the inside out.
|
||||
bodyStr := f.renderGroup(f.groupName, f.valuesStr, argsStr)
|
||||
for i := len(f.groups) - 1; i >= 0; i-- {
|
||||
grp := &f.groups[i]
|
||||
if grp.values == "" && bodyStr == "" {
|
||||
// no contents, so we must elide the whole group
|
||||
continue
|
||||
}
|
||||
bodyStr = f.renderGroup(grp.name, grp.values, bodyStr)
|
||||
}
|
||||
|
||||
if bodyStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.parentValuesStr)
|
||||
continuing = true
|
||||
}
|
||||
|
||||
groupDepth := f.groupDepth
|
||||
if f.group != "" {
|
||||
if f.valuesStr != "" || len(args) != 0 {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteByte('{') // for the group
|
||||
continuing = false
|
||||
} else {
|
||||
// The group was empty
|
||||
groupDepth--
|
||||
}
|
||||
}
|
||||
|
||||
if f.valuesStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.valuesStr)
|
||||
continuing = true
|
||||
}
|
||||
|
||||
vals = args
|
||||
if hook := f.opts.RenderArgsHook; hook != nil {
|
||||
vals = hook(f.sanitize(vals))
|
||||
}
|
||||
f.flatten(buf, vals, continuing, true) // escape user-provided keys
|
||||
|
||||
for i := 0; i < groupDepth; i++ {
|
||||
buf.WriteByte('}') // for the groups
|
||||
buf.WriteString(bodyStr)
|
||||
}
|
||||
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte('}') // for the whole line
|
||||
buf.WriteByte('}') // for the whole record
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// flatten renders a list of key-value pairs into a buffer. If continuing is
|
||||
// true, it assumes that the buffer has previous values and will emit a
|
||||
// separator (which depends on the output format) before the first pair it
|
||||
// writes. If escapeKeys is true, the keys are assumed to have
|
||||
// non-JSON-compatible characters in them and must be evaluated for escapes.
|
||||
// renderGroup returns a string representation of the named group with rendered
|
||||
// values and args. If the name is empty, this will return the values and args,
|
||||
// joined. If the name is not empty, this will return a single key-value pair,
|
||||
// where the value is a grouping of the values and args. If the values and
|
||||
// args are both empty, this will return an empty string, even if the name was
|
||||
// specified.
|
||||
func (f Formatter) renderGroup(name string, values string, args string) string {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
|
||||
needClosingBrace := false
|
||||
if name != "" && (values != "" || args != "") {
|
||||
buf.WriteString(f.quoted(name, true)) // escape user-provided keys
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteByte('{')
|
||||
needClosingBrace = true
|
||||
}
|
||||
|
||||
continuing := false
|
||||
if values != "" {
|
||||
buf.WriteString(values)
|
||||
continuing = true
|
||||
}
|
||||
|
||||
if args != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(args)
|
||||
}
|
||||
|
||||
if needClosingBrace {
|
||||
buf.WriteByte('}')
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// flatten renders a list of key-value pairs into a buffer. If escapeKeys is
|
||||
// true, the keys are assumed to have non-JSON-compatible characters in them
|
||||
// and must be evaluated for escapes.
|
||||
//
|
||||
// This function returns a potentially modified version of kvList, which
|
||||
// ensures that there is a value for every key (adding a value if needed) and
|
||||
// that each key is a string (substituting a key if needed).
|
||||
func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, escapeKeys bool) []any {
|
||||
func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, escapeKeys bool) []any {
|
||||
// This logic overlaps with sanitize() but saves one type-cast per key,
|
||||
// which can be measurable.
|
||||
if len(kvList)%2 != 0 {
|
||||
@@ -354,7 +386,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
|
||||
}
|
||||
v := kvList[i+1]
|
||||
|
||||
if i > 0 || continuing {
|
||||
if i > 0 {
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte(f.comma())
|
||||
} else {
|
||||
@@ -766,46 +798,17 @@ func (f Formatter) sanitize(kvList []any) []any {
|
||||
// startGroup opens a new group scope (basically a sub-struct), which locks all
|
||||
// the current saved values and starts them anew. This is needed to satisfy
|
||||
// slog.
|
||||
func (f *Formatter) startGroup(group string) {
|
||||
func (f *Formatter) startGroup(name string) {
|
||||
// Unnamed groups are just inlined.
|
||||
if group == "" {
|
||||
if name == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Any saved values can no longer be changed.
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
continuing := false
|
||||
|
||||
if f.parentValuesStr != "" {
|
||||
buf.WriteString(f.parentValuesStr)
|
||||
continuing = true
|
||||
}
|
||||
|
||||
if f.group != "" && f.valuesStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteByte('{') // for the group
|
||||
continuing = false
|
||||
}
|
||||
|
||||
if f.valuesStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.valuesStr)
|
||||
}
|
||||
|
||||
// NOTE: We don't close the scope here - that's done later, when a log line
|
||||
// is actually rendered (because we have N scopes to close).
|
||||
|
||||
f.parentValuesStr = buf.String()
|
||||
n := len(f.groups)
|
||||
f.groups = append(f.groups[:n:n], groupDef{f.groupName, f.valuesStr})
|
||||
|
||||
// Start collecting new values.
|
||||
f.group = group
|
||||
f.groupDepth++
|
||||
f.groupName = name
|
||||
f.valuesStr = ""
|
||||
f.values = nil
|
||||
}
|
||||
@@ -900,7 +903,7 @@ func (f *Formatter) AddValues(kvList []any) {
|
||||
|
||||
// Pre-render values, so we don't have to do it on each Info/Error call.
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
f.flatten(buf, vals, false, true) // escape user-provided keys
|
||||
f.flatten(buf, vals, true) // escape user-provided keys
|
||||
f.valuesStr = buf.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# Changelog
|
||||
|
||||
## Release 3.2.3 (2022-11-29)
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated docs (thanks @book987 @aJetHorn @neelayu @pellizzetti @apricote @SaigyoujiYuyuko233 @AlekSi)
|
||||
- #348: Updated huandu/xstrings which fixed a snake case bug (thanks @yxxhero)
|
||||
- #353: Updated masterminds/semver which included bug fixes
|
||||
- #354: Updated golang.org/x/crypto which included bug fixes
|
||||
|
||||
## Release 3.2.2 (2021-02-04)
|
||||
|
||||
This is a re-release of 3.2.1 to satisfy something with the Go module system.
|
||||
|
||||
## Release 3.2.1 (2021-02-04)
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgraded `Masterminds/goutils` to `v1.1.1`. see the [Security Advisory](https://github.com/Masterminds/goutils/security/advisories/GHSA-xg2h-wx96-xgxr)
|
||||
|
||||
## Release 3.2.0 (2020-12-14)
|
||||
|
||||
### Added
|
||||
@@ -1,4 +1,4 @@
|
||||
# Slim-Sprig: Template functions for Go templates [](https://godoc.org/github.com/go-task/slim-sprig) [](https://goreportcard.com/report/github.com/go-task/slim-sprig)
|
||||
# Slim-Sprig: Template functions for Go templates [](https://pkg.go.dev/github.com/go-task/slim-sprig/v3)
|
||||
|
||||
Slim-Sprig is a fork of [Sprig](https://github.com/Masterminds/sprig), but with
|
||||
all functions that depend on external (non standard library) or crypto packages
|
||||
@@ -1,6 +1,6 @@
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '2'
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
8
vendor/github.com/google/pprof/profile/encode.go
generated
vendored
8
vendor/github.com/google/pprof/profile/encode.go
generated
vendored
@@ -122,6 +122,7 @@ func (p *Profile) preEncode() {
|
||||
}
|
||||
|
||||
p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
|
||||
p.docURLX = addString(strings, p.DocURL)
|
||||
|
||||
p.stringTable = make([]string, len(strings))
|
||||
for s, i := range strings {
|
||||
@@ -156,6 +157,7 @@ func (p *Profile) encode(b *buffer) {
|
||||
encodeInt64Opt(b, 12, p.Period)
|
||||
encodeInt64s(b, 13, p.commentX)
|
||||
encodeInt64(b, 14, p.defaultSampleTypeX)
|
||||
encodeInt64Opt(b, 15, p.docURLX)
|
||||
}
|
||||
|
||||
var profileDecoder = []decoder{
|
||||
@@ -237,6 +239,8 @@ var profileDecoder = []decoder{
|
||||
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
|
||||
// int64 defaultSampleType = 14
|
||||
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) },
|
||||
// string doc_link = 15;
|
||||
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).docURLX) },
|
||||
}
|
||||
|
||||
// postDecode takes the unexported fields populated by decode (with
|
||||
@@ -384,6 +388,7 @@ func (p *Profile) postDecode() error {
|
||||
|
||||
p.commentX = nil
|
||||
p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
|
||||
p.DocURL, err = getString(p.stringTable, &p.docURLX, err)
|
||||
p.stringTable = nil
|
||||
return err
|
||||
}
|
||||
@@ -530,6 +535,7 @@ func (p *Line) decoder() []decoder {
|
||||
func (p *Line) encode(b *buffer) {
|
||||
encodeUint64Opt(b, 1, p.functionIDX)
|
||||
encodeInt64Opt(b, 2, p.Line)
|
||||
encodeInt64Opt(b, 3, p.Column)
|
||||
}
|
||||
|
||||
var lineDecoder = []decoder{
|
||||
@@ -538,6 +544,8 @@ var lineDecoder = []decoder{
|
||||
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) },
|
||||
// optional int64 line = 2
|
||||
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) },
|
||||
// optional int64 column = 3
|
||||
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Column) },
|
||||
}
|
||||
|
||||
func (p *Function) decoder() []decoder {
|
||||
|
||||
4
vendor/github.com/google/pprof/profile/legacy_java_profile.go
generated
vendored
4
vendor/github.com/google/pprof/profile/legacy_java_profile.go
generated
vendored
@@ -56,7 +56,7 @@ func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte
|
||||
}
|
||||
|
||||
// Strip out addresses for better merge.
|
||||
if err = p.Aggregate(true, true, true, true, false); err != nil {
|
||||
if err = p.Aggregate(true, true, true, true, false, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func parseJavaProfile(b []byte) (*Profile, error) {
|
||||
}
|
||||
|
||||
// Strip out addresses for better merge.
|
||||
if err = p.Aggregate(true, true, true, true, false); err != nil {
|
||||
if err = p.Aggregate(true, true, true, true, false, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
9
vendor/github.com/google/pprof/profile/merge.go
generated
vendored
9
vendor/github.com/google/pprof/profile/merge.go
generated
vendored
@@ -326,12 +326,13 @@ func (l *Location) key() locationKey {
|
||||
key.addr -= l.Mapping.Start
|
||||
key.mappingID = l.Mapping.ID
|
||||
}
|
||||
lines := make([]string, len(l.Line)*2)
|
||||
lines := make([]string, len(l.Line)*3)
|
||||
for i, line := range l.Line {
|
||||
if line.Function != nil {
|
||||
lines[i*2] = strconv.FormatUint(line.Function.ID, 16)
|
||||
}
|
||||
lines[i*2+1] = strconv.FormatInt(line.Line, 16)
|
||||
lines[i*2+2] = strconv.FormatInt(line.Column, 16)
|
||||
}
|
||||
key.lines = strings.Join(lines, "|")
|
||||
return key
|
||||
@@ -418,6 +419,7 @@ func (pm *profileMerger) mapLine(src Line) Line {
|
||||
ln := Line{
|
||||
Function: pm.mapFunction(src.Function),
|
||||
Line: src.Line,
|
||||
Column: src.Column,
|
||||
}
|
||||
return ln
|
||||
}
|
||||
@@ -474,6 +476,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||
var timeNanos, durationNanos, period int64
|
||||
var comments []string
|
||||
seenComments := map[string]bool{}
|
||||
var docURL string
|
||||
var defaultSampleType string
|
||||
for _, s := range srcs {
|
||||
if timeNanos == 0 || s.TimeNanos < timeNanos {
|
||||
@@ -492,6 +495,9 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||
if defaultSampleType == "" {
|
||||
defaultSampleType = s.DefaultSampleType
|
||||
}
|
||||
if docURL == "" {
|
||||
docURL = s.DocURL
|
||||
}
|
||||
}
|
||||
|
||||
p := &Profile{
|
||||
@@ -507,6 +513,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||
|
||||
Comments: comments,
|
||||
DefaultSampleType: defaultSampleType,
|
||||
DocURL: docURL,
|
||||
}
|
||||
copy(p.SampleType, srcs[0].SampleType)
|
||||
return p, nil
|
||||
|
||||
23
vendor/github.com/google/pprof/profile/profile.go
generated
vendored
23
vendor/github.com/google/pprof/profile/profile.go
generated
vendored
@@ -39,6 +39,7 @@ type Profile struct {
|
||||
Location []*Location
|
||||
Function []*Function
|
||||
Comments []string
|
||||
DocURL string
|
||||
|
||||
DropFrames string
|
||||
KeepFrames string
|
||||
@@ -53,6 +54,7 @@ type Profile struct {
|
||||
encodeMu sync.Mutex
|
||||
|
||||
commentX []int64
|
||||
docURLX int64
|
||||
dropFramesX int64
|
||||
keepFramesX int64
|
||||
stringTable []string
|
||||
@@ -145,6 +147,7 @@ type Location struct {
|
||||
type Line struct {
|
||||
Function *Function
|
||||
Line int64
|
||||
Column int64
|
||||
|
||||
functionIDX uint64
|
||||
}
|
||||
@@ -436,7 +439,7 @@ func (p *Profile) CheckValid() error {
|
||||
// Aggregate merges the locations in the profile into equivalence
|
||||
// classes preserving the request attributes. It also updates the
|
||||
// samples to point to the merged locations.
|
||||
func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error {
|
||||
func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, columnnumber, address bool) error {
|
||||
for _, m := range p.Mapping {
|
||||
m.HasInlineFrames = m.HasInlineFrames && inlineFrame
|
||||
m.HasFunctions = m.HasFunctions && function
|
||||
@@ -458,7 +461,7 @@ func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address
|
||||
}
|
||||
|
||||
// Aggregate locations
|
||||
if !inlineFrame || !address || !linenumber {
|
||||
if !inlineFrame || !address || !linenumber || !columnnumber {
|
||||
for _, l := range p.Location {
|
||||
if !inlineFrame && len(l.Line) > 1 {
|
||||
l.Line = l.Line[len(l.Line)-1:]
|
||||
@@ -466,6 +469,12 @@ func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address
|
||||
if !linenumber {
|
||||
for i := range l.Line {
|
||||
l.Line[i].Line = 0
|
||||
l.Line[i].Column = 0
|
||||
}
|
||||
}
|
||||
if !columnnumber {
|
||||
for i := range l.Line {
|
||||
l.Line[i].Column = 0
|
||||
}
|
||||
}
|
||||
if !address {
|
||||
@@ -548,6 +557,9 @@ func (p *Profile) String() string {
|
||||
for _, c := range p.Comments {
|
||||
ss = append(ss, "Comment: "+c)
|
||||
}
|
||||
if url := p.DocURL; url != "" {
|
||||
ss = append(ss, fmt.Sprintf("Doc: %s", url))
|
||||
}
|
||||
if pt := p.PeriodType; pt != nil {
|
||||
ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
|
||||
}
|
||||
@@ -627,10 +639,11 @@ func (l *Location) string() string {
|
||||
for li := range l.Line {
|
||||
lnStr := "??"
|
||||
if fn := l.Line[li].Function; fn != nil {
|
||||
lnStr = fmt.Sprintf("%s %s:%d s=%d",
|
||||
lnStr = fmt.Sprintf("%s %s:%d:%d s=%d",
|
||||
fn.Name,
|
||||
fn.Filename,
|
||||
l.Line[li].Line,
|
||||
l.Line[li].Column,
|
||||
fn.StartLine)
|
||||
if fn.Name != fn.SystemName {
|
||||
lnStr = lnStr + "(" + fn.SystemName + ")"
|
||||
@@ -836,10 +849,10 @@ func (p *Profile) HasFileLines() bool {
|
||||
|
||||
// Unsymbolizable returns true if a mapping points to a binary for which
|
||||
// locations can't be symbolized in principle, at least now. Examples are
|
||||
// "[vdso]", [vsyscall]" and some others, see the code.
|
||||
// "[vdso]", "[vsyscall]" and some others, see the code.
|
||||
func (m *Mapping) Unsymbolizable() bool {
|
||||
name := filepath.Base(m.File)
|
||||
return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/")
|
||||
return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") || m.File == "//anon"
|
||||
}
|
||||
|
||||
// Copy makes a fully independent copy of a profile.
|
||||
|
||||
16
vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go
generated
vendored
16
vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go
generated
vendored
@@ -24,15 +24,15 @@ const (
|
||||
|
||||
var SingletonFormatter = New(ColorModeTerminal)
|
||||
|
||||
func F(format string, args ...interface{}) string {
|
||||
func F(format string, args ...any) string {
|
||||
return SingletonFormatter.F(format, args...)
|
||||
}
|
||||
|
||||
func Fi(indentation uint, format string, args ...interface{}) string {
|
||||
func Fi(indentation uint, format string, args ...any) string {
|
||||
return SingletonFormatter.Fi(indentation, format, args...)
|
||||
}
|
||||
|
||||
func Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string {
|
||||
func Fiw(indentation uint, maxWidth uint, format string, args ...any) string {
|
||||
return SingletonFormatter.Fiw(indentation, maxWidth, format, args...)
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ func New(colorMode ColorMode) Formatter {
|
||||
return fmt.Sprintf("\x1b[38;5;%dm", colorCode)
|
||||
}
|
||||
|
||||
if _, noColor := os.LookupEnv("GINKGO_NO_COLOR"); noColor {
|
||||
colorMode = ColorModeNone
|
||||
}
|
||||
|
||||
f := Formatter{
|
||||
ColorMode: colorMode,
|
||||
colors: map[string]string{
|
||||
@@ -111,15 +115,15 @@ func New(colorMode ColorMode) Formatter {
|
||||
return f
|
||||
}
|
||||
|
||||
func (f Formatter) F(format string, args ...interface{}) string {
|
||||
func (f Formatter) F(format string, args ...any) string {
|
||||
return f.Fi(0, format, args...)
|
||||
}
|
||||
|
||||
func (f Formatter) Fi(indentation uint, format string, args ...interface{}) string {
|
||||
func (f Formatter) Fi(indentation uint, format string, args ...any) string {
|
||||
return f.Fiw(indentation, 0, format, args...)
|
||||
}
|
||||
|
||||
func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string {
|
||||
func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...any) string {
|
||||
out := f.style(format)
|
||||
if len(args) > 0 {
|
||||
out = fmt.Sprintf(out, args...)
|
||||
|
||||
21
vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go
generated
vendored
21
vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go
generated
vendored
@@ -2,6 +2,8 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
@@ -42,7 +44,7 @@ func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.Go
|
||||
internal.VerifyCLIAndFrameworkVersion(suites)
|
||||
|
||||
opc := internal.NewOrderedParallelCompiler(cliConfig.ComputedNumCompilers())
|
||||
opc.StartCompiling(suites, goFlagsConfig)
|
||||
opc.StartCompiling(suites, goFlagsConfig, true)
|
||||
|
||||
for {
|
||||
suiteIdx, suite := opc.Next()
|
||||
@@ -53,7 +55,22 @@ func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.Go
|
||||
if suite.State.Is(internal.TestSuiteStateFailedToCompile) {
|
||||
fmt.Println(suite.CompilationError.Error())
|
||||
} else {
|
||||
fmt.Printf("Compiled %s.test\n", suite.PackageName)
|
||||
var testBinPath string
|
||||
if len(goFlagsConfig.O) != 0 {
|
||||
stat, err := os.Stat(goFlagsConfig.O)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if stat.IsDir() {
|
||||
testBinPath = goFlagsConfig.O + "/" + suite.PackageName + ".test"
|
||||
} else {
|
||||
testBinPath = goFlagsConfig.O
|
||||
}
|
||||
}
|
||||
if len(testBinPath) == 0 {
|
||||
testBinPath = path.Join(suite.Path, suite.PackageName+".test")
|
||||
}
|
||||
fmt.Printf("Compiled %s\n", testBinPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go
generated
vendored
6
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go
generated
vendored
@@ -12,7 +12,7 @@ func Abort(details AbortDetails) {
|
||||
panic(details)
|
||||
}
|
||||
|
||||
func AbortGracefullyWith(format string, args ...interface{}) {
|
||||
func AbortGracefullyWith(format string, args ...any) {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 0,
|
||||
Error: fmt.Errorf(format, args...),
|
||||
@@ -20,7 +20,7 @@ func AbortGracefullyWith(format string, args ...interface{}) {
|
||||
})
|
||||
}
|
||||
|
||||
func AbortWith(format string, args ...interface{}) {
|
||||
func AbortWith(format string, args ...any) {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 1,
|
||||
Error: fmt.Errorf(format, args...),
|
||||
@@ -28,7 +28,7 @@ func AbortWith(format string, args ...interface{}) {
|
||||
})
|
||||
}
|
||||
|
||||
func AbortWithUsage(format string, args ...interface{}) {
|
||||
func AbortWithUsage(format string, args ...any) {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 1,
|
||||
Error: fmt.Errorf(format, args...),
|
||||
|
||||
6
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go
generated
vendored
6
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go
generated
vendored
@@ -24,7 +24,11 @@ func (c Command) Run(args []string, additionalArgs []string) {
|
||||
if err != nil {
|
||||
AbortWithUsage(err.Error())
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
if len(arg) > 1 && strings.HasPrefix(arg, "-") {
|
||||
AbortWith(types.GinkgoErrors.FlagAfterPositionalParameter().Error())
|
||||
}
|
||||
}
|
||||
c.Command(args, additionalArgs)
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go
generated
vendored
@@ -68,7 +68,6 @@ func (p Program) RunAndExit(osArgs []string) {
|
||||
fmt.Fprintln(p.ErrWriter, deprecationTracker.DeprecationsReport())
|
||||
}
|
||||
p.Exiter(exitCode)
|
||||
return
|
||||
}()
|
||||
|
||||
args, additionalArgs := []string{}, []string{}
|
||||
@@ -157,7 +156,6 @@ func (p Program) handleHelpRequestsAndExit(writer io.Writer, args []string) {
|
||||
p.EmitUsage(writer)
|
||||
Abort(AbortDetails{ExitCode: 1})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) EmitUsage(writer io.Writer) {
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig"
|
||||
sprig "github.com/go-task/slim-sprig/v3"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
|
||||
3
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
generated
vendored
3
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
generated
vendored
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig"
|
||||
sprig "github.com/go-task/slim-sprig/v3"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
@@ -174,6 +174,7 @@ func moduleName(modRoot string) string {
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer modFile.Close()
|
||||
|
||||
mod := make([]byte, 128)
|
||||
_, err = modFile.Read(mod)
|
||||
|
||||
20
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go
generated
vendored
20
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go
generated
vendored
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite {
|
||||
func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig, preserveSymbols bool) TestSuite {
|
||||
if suite.PathToCompiledTest != "" {
|
||||
return suite
|
||||
}
|
||||
@@ -25,6 +25,18 @@ func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite
|
||||
return suite
|
||||
}
|
||||
|
||||
if len(goFlagsConfig.O) > 0 {
|
||||
userDefinedPath, err := filepath.Abs(goFlagsConfig.O)
|
||||
if err != nil {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to compute compilation target path %s:\n%s", goFlagsConfig.O, err.Error())
|
||||
return suite
|
||||
}
|
||||
path = userDefinedPath
|
||||
}
|
||||
|
||||
goFlagsConfig.O = path
|
||||
|
||||
ginkgoInvocationPath, _ := os.Getwd()
|
||||
ginkgoInvocationPath, _ = filepath.Abs(ginkgoInvocationPath)
|
||||
packagePath := suite.AbsPath()
|
||||
@@ -34,7 +46,7 @@ func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite
|
||||
suite.CompilationError = fmt.Errorf("Failed to get relative path from package to the current working directory:\n%s", err.Error())
|
||||
return suite
|
||||
}
|
||||
args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, path, "./", pathToInvocationPath)
|
||||
args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, "./", pathToInvocationPath, preserveSymbols)
|
||||
if err != nil {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error())
|
||||
@@ -108,7 +120,7 @@ func NewOrderedParallelCompiler(numCompilers int) *OrderedParallelCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig) {
|
||||
func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig, preserveSymbols bool) {
|
||||
opc.stopped = false
|
||||
opc.idx = 0
|
||||
opc.numSuites = len(suites)
|
||||
@@ -123,7 +135,7 @@ func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsCon
|
||||
stopped := opc.stopped
|
||||
opc.mutex.Unlock()
|
||||
if !stopped {
|
||||
suite = CompileSuite(suite, goFlagsConfig)
|
||||
suite = CompileSuite(suite, goFlagsConfig, preserveSymbols)
|
||||
}
|
||||
c <- suite
|
||||
}
|
||||
|
||||
129
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/gocovmerge.go
generated
vendored
Normal file
129
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/gocovmerge.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2015, Wade Simmons
|
||||
// All rights reserved.
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package gocovmerge takes the results from multiple `go test -coverprofile`
|
||||
// runs and merges them into one profile
|
||||
|
||||
// this file was originally taken from the gocovmerge project
|
||||
// see also: https://go.shabbyrobe.org/gocovmerge
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
func AddCoverProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
|
||||
i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
|
||||
if i < len(profiles) && profiles[i].FileName == p.FileName {
|
||||
MergeCoverProfiles(profiles[i], p)
|
||||
} else {
|
||||
profiles = append(profiles, nil)
|
||||
copy(profiles[i+1:], profiles[i:])
|
||||
profiles[i] = p
|
||||
}
|
||||
return profiles
|
||||
}
|
||||
|
||||
func DumpCoverProfiles(profiles []*cover.Profile, out io.Writer) error {
|
||||
if len(profiles) == 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err := fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range profiles {
|
||||
for _, b := range p.Blocks {
|
||||
if _, err := fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MergeCoverProfiles(into *cover.Profile, merge *cover.Profile) error {
|
||||
if into.Mode != merge.Mode {
|
||||
return fmt.Errorf("cannot merge profiles with different modes")
|
||||
}
|
||||
// Since the blocks are sorted, we can keep track of where the last block
|
||||
// was inserted and only look at the blocks after that as targets for merge
|
||||
startIndex := 0
|
||||
for _, b := range merge.Blocks {
|
||||
var err error
|
||||
startIndex, err = mergeProfileBlock(into, b, startIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) (int, error) {
|
||||
sortFunc := func(i int) bool {
|
||||
pi := p.Blocks[i+startIndex]
|
||||
return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
|
||||
}
|
||||
|
||||
i := 0
|
||||
if !sortFunc(i) {
|
||||
i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
|
||||
}
|
||||
|
||||
i += startIndex
|
||||
if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
|
||||
if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
|
||||
return i, fmt.Errorf("gocovmerge: overlapping merge %v %v %v", p.FileName, p.Blocks[i], pb)
|
||||
}
|
||||
switch p.Mode {
|
||||
case "set":
|
||||
p.Blocks[i].Count |= pb.Count
|
||||
case "count", "atomic":
|
||||
p.Blocks[i].Count += pb.Count
|
||||
default:
|
||||
return i, fmt.Errorf("gocovmerge: unsupported covermode '%s'", p.Mode)
|
||||
}
|
||||
|
||||
} else {
|
||||
if i > 0 {
|
||||
pa := p.Blocks[i-1]
|
||||
if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
|
||||
return i, fmt.Errorf("gocovmerge: overlap before %v %v %v", p.FileName, pa, pb)
|
||||
}
|
||||
}
|
||||
if i < len(p.Blocks)-1 {
|
||||
pa := p.Blocks[i+1]
|
||||
if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
|
||||
return i, fmt.Errorf("gocovmerge: overlap after %v %v %v", p.FileName, pa, pb)
|
||||
}
|
||||
}
|
||||
p.Blocks = append(p.Blocks, cover.ProfileBlock{})
|
||||
copy(p.Blocks[i+1:], p.Blocks[i:])
|
||||
p.Blocks[i] = pb
|
||||
}
|
||||
|
||||
return i + 1, nil
|
||||
}
|
||||
46
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go
generated
vendored
46
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go
generated
vendored
@@ -1,7 +1,6 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"github.com/google/pprof/profile"
|
||||
"github.com/onsi/ginkgo/v2/reporters"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
func AbsPathForGeneratedAsset(assetName string, suite TestSuite, cliConfig types.CLIConfig, process int) string {
|
||||
@@ -144,38 +144,27 @@ func FinalizeProfilesAndReportsForSuites(suites TestSuites, cliConfig types.CLIC
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
//loads each profile, combines them, deletes them, stores them in destination
|
||||
// loads each profile, merges them, deletes them, stores them in destination
|
||||
func MergeAndCleanupCoverProfiles(profiles []string, destination string) error {
|
||||
combined := &bytes.Buffer{}
|
||||
modeRegex := regexp.MustCompile(`^mode: .*\n`)
|
||||
for i, profile := range profiles {
|
||||
contents, err := os.ReadFile(profile)
|
||||
var merged []*cover.Profile
|
||||
for _, file := range profiles {
|
||||
parsedProfiles, err := cover.ParseProfiles(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read coverage file %s:\n%s", profile, err.Error())
|
||||
return err
|
||||
}
|
||||
os.Remove(profile)
|
||||
|
||||
// remove the cover mode line from every file
|
||||
// except the first one
|
||||
if i > 0 {
|
||||
contents = modeRegex.ReplaceAll(contents, []byte{})
|
||||
}
|
||||
|
||||
_, err = combined.Write(contents)
|
||||
|
||||
// Add a newline to the end of every file if missing.
|
||||
if err == nil && len(contents) > 0 && contents[len(contents)-1] != '\n' {
|
||||
_, err = combined.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to append to coverprofile:\n%s", err.Error())
|
||||
os.Remove(file)
|
||||
for _, p := range parsedProfiles {
|
||||
merged = AddCoverProfile(merged, p)
|
||||
}
|
||||
}
|
||||
|
||||
err := os.WriteFile(destination, combined.Bytes(), 0666)
|
||||
dst, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create combined cover profile:\n%s", err.Error())
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
err = DumpCoverProfiles(merged, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -184,7 +173,7 @@ func GetCoverageFromCoverProfile(profile string) (float64, error) {
|
||||
cmd := exec.Command("go", "tool", "cover", "-func", profile)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Could not process Coverprofile %s: %s", profile, err.Error())
|
||||
return 0, fmt.Errorf("Could not process Coverprofile %s: %s - %s", profile, err.Error(), string(output))
|
||||
}
|
||||
re := regexp.MustCompile(`total:\s*\(statements\)\s*(\d*\.\d*)\%`)
|
||||
matches := re.FindStringSubmatch(string(output))
|
||||
@@ -208,6 +197,7 @@ func MergeProfiles(profilePaths []string, destination string) error {
|
||||
return fmt.Errorf("Could not open profile: %s\n%s", profilePath, err.Error())
|
||||
}
|
||||
prof, err := profile.Parse(proFile)
|
||||
_ = proFile.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not parse profile: %s\n%s", profilePath, err.Error())
|
||||
}
|
||||
|
||||
9
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go
generated
vendored
9
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go
generated
vendored
@@ -7,6 +7,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
@@ -192,7 +193,7 @@ func precompiledTestSuite(path string) (TestSuite, error) {
|
||||
return TestSuite{}, errors.New("this is not a .test binary")
|
||||
}
|
||||
|
||||
if filepath.Ext(path) == ".test" && info.Mode()&0111 == 0 {
|
||||
if filepath.Ext(path) == ".test" && runtime.GOOS != "windows" && info.Mode()&0111 == 0 {
|
||||
return TestSuite{}, errors.New("this is not executable")
|
||||
}
|
||||
|
||||
@@ -225,7 +226,7 @@ func suitesInDir(dir string, recurse bool) TestSuites {
|
||||
files, _ := os.ReadDir(dir)
|
||||
re := regexp.MustCompile(`^[^._].*_test\.go$`)
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && re.Match([]byte(file.Name())) {
|
||||
if !file.IsDir() && re.MatchString(file.Name()) {
|
||||
suite := TestSuite{
|
||||
Path: relPath(dir),
|
||||
PackageName: packageNameForSuite(dir),
|
||||
@@ -240,7 +241,7 @@ func suitesInDir(dir string, recurse bool) TestSuites {
|
||||
if recurse {
|
||||
re = regexp.MustCompile(`^[._]`)
|
||||
for _, file := range files {
|
||||
if file.IsDir() && !re.Match([]byte(file.Name())) {
|
||||
if file.IsDir() && !re.MatchString(file.Name()) {
|
||||
suites = append(suites, suitesInDir(dir+"/"+file.Name(), recurse)...)
|
||||
}
|
||||
}
|
||||
@@ -271,7 +272,7 @@ func filesHaveGinkgoSuite(dir string, files []os.DirEntry) bool {
|
||||
reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"|\/ginkgo\/v2"|\/ginkgo\/v2/dsl/`)
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && reTestFile.Match([]byte(file.Name())) {
|
||||
if !file.IsDir() && reTestFile.MatchString(file.Name()) {
|
||||
contents, _ := os.ReadFile(dir + "/" + file.Name())
|
||||
if reGinkgo.Match(contents) {
|
||||
return true
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go
generated
vendored
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "go.uber.org/automaxprocs"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/build"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/generators"
|
||||
|
||||
3
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
generated
vendored
3
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
generated
vendored
@@ -1,10 +1,11 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
9
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go
generated
vendored
9
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go
generated
vendored
@@ -28,14 +28,7 @@ func packageNameForImport(f *ast.File, path string) *string {
|
||||
}
|
||||
name := spec.Name.String()
|
||||
if name == "<nil>" {
|
||||
// If the package name is not explicitly specified,
|
||||
// make an educated guess. This is not guaranteed to be correct.
|
||||
lastSlash := strings.LastIndex(path, "/")
|
||||
if lastSlash == -1 {
|
||||
name = path
|
||||
} else {
|
||||
name = path[lastSlash+1:]
|
||||
}
|
||||
name = "ginkgo"
|
||||
}
|
||||
if name == "." {
|
||||
name = ""
|
||||
|
||||
26
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
generated
vendored
26
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
generated
vendored
@@ -1,10 +1,13 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
@@ -84,9 +87,11 @@ func (o *outline) String() string {
|
||||
// StringIndent returns a CSV-formated outline, but every line is indented by
|
||||
// one 'width' of spaces for every level of nesting.
|
||||
func (o *outline) StringIndent(width int) string {
|
||||
var b strings.Builder
|
||||
var b bytes.Buffer
|
||||
b.WriteString("Name,Text,Start,End,Spec,Focused,Pending,Labels\n")
|
||||
|
||||
csvWriter := csv.NewWriter(&b)
|
||||
|
||||
currentIndent := 0
|
||||
pre := func(n *ginkgoNode) {
|
||||
b.WriteString(fmt.Sprintf("%*s", currentIndent, ""))
|
||||
@@ -96,8 +101,22 @@ func (o *outline) StringIndent(width int) string {
|
||||
} else {
|
||||
labels = strings.Join(n.Labels, ", ")
|
||||
}
|
||||
//enclosing labels in a double quoted comma separate listed so that when inmported into a CSV app the Labels column has comma separate strings
|
||||
b.WriteString(fmt.Sprintf("%s,%s,%d,%d,%t,%t,%t,\"%s\"\n", n.Name, n.Text, n.Start, n.End, n.Spec, n.Focused, n.Pending, labels))
|
||||
|
||||
row := []string{
|
||||
n.Name,
|
||||
n.Text,
|
||||
strconv.Itoa(n.Start),
|
||||
strconv.Itoa(n.End),
|
||||
strconv.FormatBool(n.Spec),
|
||||
strconv.FormatBool(n.Focused),
|
||||
strconv.FormatBool(n.Pending),
|
||||
labels,
|
||||
}
|
||||
csvWriter.Write(row)
|
||||
|
||||
// Ensure we write to `b' before the next `b.WriteString()', which might be adding indentation
|
||||
csvWriter.Flush()
|
||||
|
||||
currentIndent += width
|
||||
}
|
||||
post := func(n *ginkgoNode) {
|
||||
@@ -106,5 +125,6 @@ func (o *outline) StringIndent(width int) string {
|
||||
for _, n := range o.Nodes {
|
||||
n.Walk(pre, post)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
4
vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go
generated
vendored
4
vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go
generated
vendored
@@ -107,7 +107,7 @@ OUTER_LOOP:
|
||||
}
|
||||
|
||||
opc := internal.NewOrderedParallelCompiler(r.cliConfig.ComputedNumCompilers())
|
||||
opc.StartCompiling(suites, r.goFlagsConfig)
|
||||
opc.StartCompiling(suites, r.goFlagsConfig, false)
|
||||
|
||||
SUITE_LOOP:
|
||||
for {
|
||||
@@ -142,7 +142,7 @@ OUTER_LOOP:
|
||||
}
|
||||
|
||||
if !endTime.IsZero() {
|
||||
r.suiteConfig.Timeout = endTime.Sub(time.Now())
|
||||
r.suiteConfig.Timeout = time.Until(endTime)
|
||||
if r.suiteConfig.Timeout <= 0 {
|
||||
suites[suiteIdx].State = internal.TestSuiteStateFailedDueToTimeout
|
||||
opc.StopAndDrain()
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go
generated
vendored
@@ -78,7 +78,7 @@ func (d Dependencies) resolveAndAdd(deps []string, depth int) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !pkg.Goroot && (!ginkgoAndGomegaFilter.Match([]byte(pkg.Dir)) || ginkgoIntegrationTestFilter.Match([]byte(pkg.Dir))) {
|
||||
if !pkg.Goroot && (!ginkgoAndGomegaFilter.MatchString(pkg.Dir) || ginkgoIntegrationTestFilter.MatchString(pkg.Dir)) {
|
||||
d.addDepIfNotPresent(pkg.Dir, depth)
|
||||
}
|
||||
}
|
||||
|
||||
13
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
generated
vendored
13
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -79,7 +80,11 @@ func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Ti
|
||||
continue
|
||||
}
|
||||
|
||||
if goTestRegExp.Match([]byte(info.Name())) {
|
||||
if isHiddenFile(info) {
|
||||
continue
|
||||
}
|
||||
|
||||
if goTestRegExp.MatchString(info.Name()) {
|
||||
testHash += p.hashForFileInfo(info)
|
||||
if info.ModTime().After(testModifiedTime) {
|
||||
testModifiedTime = info.ModTime()
|
||||
@@ -87,7 +92,7 @@ func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Ti
|
||||
continue
|
||||
}
|
||||
|
||||
if p.watchRegExp.Match([]byte(info.Name())) {
|
||||
if p.watchRegExp.MatchString(info.Name()) {
|
||||
codeHash += p.hashForFileInfo(info)
|
||||
if info.ModTime().After(codeModifiedTime) {
|
||||
codeModifiedTime = info.ModTime()
|
||||
@@ -103,6 +108,10 @@ func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Ti
|
||||
return
|
||||
}
|
||||
|
||||
func isHiddenFile(info os.FileInfo) bool {
|
||||
return strings.HasPrefix(info.Name(), ".") || strings.HasPrefix(info.Name(), "_")
|
||||
}
|
||||
|
||||
func (p *PackageHash) hashForFileInfo(info os.FileInfo) string {
|
||||
return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano())
|
||||
}
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go
generated
vendored
@@ -153,7 +153,7 @@ func (w *SpecWatcher) WatchSpecs(args []string, additionalArgs []string) {
|
||||
}
|
||||
|
||||
func (w *SpecWatcher) compileAndRun(suite internal.TestSuite, additionalArgs []string) internal.TestSuite {
|
||||
suite = internal.CompileSuite(suite, w.goFlagsConfig)
|
||||
suite = internal.CompileSuite(suite, w.goFlagsConfig, false)
|
||||
if suite.State.Is(internal.TestSuiteStateFailedToCompile) {
|
||||
fmt.Println(suite.CompilationError.Error())
|
||||
return suite
|
||||
|
||||
22
vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go
generated
vendored
22
vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go
generated
vendored
@@ -40,7 +40,7 @@ func (ic InterruptCause) String() string {
|
||||
}
|
||||
|
||||
type InterruptStatus struct {
|
||||
Channel chan interface{}
|
||||
Channel chan any
|
||||
Level InterruptLevel
|
||||
Cause InterruptCause
|
||||
}
|
||||
@@ -62,14 +62,14 @@ type InterruptHandlerInterface interface {
|
||||
}
|
||||
|
||||
type InterruptHandler struct {
|
||||
c chan interface{}
|
||||
c chan any
|
||||
lock *sync.Mutex
|
||||
level InterruptLevel
|
||||
cause InterruptCause
|
||||
client parallel_support.Client
|
||||
stop chan interface{}
|
||||
stop chan any
|
||||
signals []os.Signal
|
||||
requestAbortCheck chan interface{}
|
||||
requestAbortCheck chan any
|
||||
}
|
||||
|
||||
func NewInterruptHandler(client parallel_support.Client, signals ...os.Signal) *InterruptHandler {
|
||||
@@ -77,10 +77,10 @@ func NewInterruptHandler(client parallel_support.Client, signals ...os.Signal) *
|
||||
signals = []os.Signal{os.Interrupt, syscall.SIGTERM}
|
||||
}
|
||||
handler := &InterruptHandler{
|
||||
c: make(chan interface{}),
|
||||
c: make(chan any),
|
||||
lock: &sync.Mutex{},
|
||||
stop: make(chan interface{}),
|
||||
requestAbortCheck: make(chan interface{}),
|
||||
stop: make(chan any),
|
||||
requestAbortCheck: make(chan any),
|
||||
client: client,
|
||||
signals: signals,
|
||||
}
|
||||
@@ -98,9 +98,9 @@ func (handler *InterruptHandler) registerForInterrupts() {
|
||||
signal.Notify(signalChannel, handler.signals...)
|
||||
|
||||
// cross-process abort handling
|
||||
var abortChannel chan interface{}
|
||||
var abortChannel chan any
|
||||
if handler.client != nil {
|
||||
abortChannel = make(chan interface{})
|
||||
abortChannel = make(chan any)
|
||||
go func() {
|
||||
pollTicker := time.NewTicker(ABORT_POLLING_INTERVAL)
|
||||
for {
|
||||
@@ -125,7 +125,7 @@ func (handler *InterruptHandler) registerForInterrupts() {
|
||||
}()
|
||||
}
|
||||
|
||||
go func(abortChannel chan interface{}) {
|
||||
go func(abortChannel chan any) {
|
||||
var interruptCause InterruptCause
|
||||
for {
|
||||
select {
|
||||
@@ -151,7 +151,7 @@ func (handler *InterruptHandler) registerForInterrupts() {
|
||||
}
|
||||
if handler.level != oldLevel {
|
||||
close(handler.c)
|
||||
handler.c = make(chan interface{})
|
||||
handler.c = make(chan any)
|
||||
}
|
||||
handler.lock.Unlock()
|
||||
}
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go
generated
vendored
@@ -30,7 +30,7 @@ type Server interface {
|
||||
Close()
|
||||
Address() string
|
||||
RegisterAlive(node int, alive func() bool)
|
||||
GetSuiteDone() chan interface{}
|
||||
GetSuiteDone() chan any
|
||||
GetOutputDestination() io.Writer
|
||||
SetOutputDestination(io.Writer)
|
||||
}
|
||||
|
||||
9
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go
generated
vendored
9
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go
generated
vendored
@@ -34,7 +34,7 @@ func (client *httpClient) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *httpClient) post(path string, data interface{}) error {
|
||||
func (client *httpClient) post(path string, data any) error {
|
||||
var body io.Reader
|
||||
if data != nil {
|
||||
encoded, err := json.Marshal(data)
|
||||
@@ -54,7 +54,7 @@ func (client *httpClient) post(path string, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *httpClient) poll(path string, data interface{}) error {
|
||||
func (client *httpClient) poll(path string, data any) error {
|
||||
for {
|
||||
resp, err := http.Get(client.serverHost + path)
|
||||
if err != nil {
|
||||
@@ -153,10 +153,7 @@ func (client *httpClient) PostAbort() error {
|
||||
|
||||
func (client *httpClient) ShouldAbort() bool {
|
||||
err := client.poll("/abort", nil)
|
||||
if err == ErrorGone {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return err == ErrorGone
|
||||
}
|
||||
|
||||
func (client *httpClient) Write(p []byte) (int, error) {
|
||||
|
||||
4
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go
generated
vendored
4
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go
generated
vendored
@@ -75,7 +75,7 @@ func (server *httpServer) Address() string {
|
||||
return "http://" + server.listener.Addr().String()
|
||||
}
|
||||
|
||||
func (server *httpServer) GetSuiteDone() chan interface{} {
|
||||
func (server *httpServer) GetSuiteDone() chan any {
|
||||
return server.handler.done
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (server *httpServer) RegisterAlive(node int, alive func() bool) {
|
||||
//
|
||||
|
||||
// The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters`
|
||||
func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool {
|
||||
func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object any) bool {
|
||||
defer request.Body.Close()
|
||||
if json.NewDecoder(request.Body).Decode(object) != nil {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go
generated
vendored
@@ -35,7 +35,7 @@ func (client *rpcClient) Close() error {
|
||||
return client.client.Close()
|
||||
}
|
||||
|
||||
func (client *rpcClient) poll(method string, data interface{}) error {
|
||||
func (client *rpcClient) poll(method string, data any) error {
|
||||
for {
|
||||
err := client.client.Call(method, voidSender, data)
|
||||
if err == nil {
|
||||
|
||||
10
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go
generated
vendored
10
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go
generated
vendored
@@ -25,7 +25,7 @@ type RPCServer struct {
|
||||
handler *ServerHandler
|
||||
}
|
||||
|
||||
//Create a new server, automatically selecting a port
|
||||
// Create a new server, automatically selecting a port
|
||||
func newRPCServer(parallelTotal int, reporter reporters.Reporter) (*RPCServer, error) {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
@@ -37,7 +37,7 @@ func newRPCServer(parallelTotal int, reporter reporters.Reporter) (*RPCServer, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
//Start the server. You don't need to `go s.Start()`, just `s.Start()`
|
||||
// Start the server. You don't need to `go s.Start()`, just `s.Start()`
|
||||
func (server *RPCServer) Start() {
|
||||
rpcServer := rpc.NewServer()
|
||||
rpcServer.RegisterName("Server", server.handler) //register the handler's methods as the server
|
||||
@@ -48,17 +48,17 @@ func (server *RPCServer) Start() {
|
||||
go httpServer.Serve(server.listener)
|
||||
}
|
||||
|
||||
//Stop the server
|
||||
// Stop the server
|
||||
func (server *RPCServer) Close() {
|
||||
server.listener.Close()
|
||||
}
|
||||
|
||||
//The address the server can be reached it. Pass this into the `ForwardingReporter`.
|
||||
// The address the server can be reached it. Pass this into the `ForwardingReporter`.
|
||||
func (server *RPCServer) Address() string {
|
||||
return server.listener.Addr().String()
|
||||
}
|
||||
|
||||
func (server *RPCServer) GetSuiteDone() chan interface{} {
|
||||
func (server *RPCServer) GetSuiteDone() chan any {
|
||||
return server.handler.done
|
||||
}
|
||||
|
||||
|
||||
4
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go
generated
vendored
4
vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go
generated
vendored
@@ -18,7 +18,7 @@ var voidSender Void
|
||||
// It handles all the business logic to avoid duplication between the two servers
|
||||
|
||||
type ServerHandler struct {
|
||||
done chan interface{}
|
||||
done chan any
|
||||
outputDestination io.Writer
|
||||
reporter reporters.Reporter
|
||||
alives []func() bool
|
||||
@@ -46,7 +46,7 @@ func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHan
|
||||
|
||||
parallelTotal: parallelTotal,
|
||||
outputDestination: os.Stdout,
|
||||
done: make(chan interface{}),
|
||||
done: make(chan any),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
65
vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go
generated
vendored
65
vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go
generated
vendored
@@ -182,10 +182,31 @@ func (r *DefaultReporter) WillRun(report types.SpecReport) {
|
||||
r.emitBlock(r.f(r.codeLocationBlock(report, "{{/}}", v.Is(types.VerbosityLevelVeryVerbose), false)))
|
||||
}
|
||||
|
||||
func (r *DefaultReporter) wrapTextBlock(sectionName string, fn func()) {
|
||||
r.emitBlock("\n")
|
||||
if r.conf.GithubOutput {
|
||||
r.emitBlock(r.fi(1, "::group::%s", sectionName))
|
||||
} else {
|
||||
r.emitBlock(r.fi(1, "{{gray}}%s >>{{/}}", sectionName))
|
||||
}
|
||||
fn()
|
||||
if r.conf.GithubOutput {
|
||||
r.emitBlock(r.fi(1, "::endgroup::"))
|
||||
} else {
|
||||
r.emitBlock(r.fi(1, "{{gray}}<< %s{{/}}", sectionName))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *DefaultReporter) DidRun(report types.SpecReport) {
|
||||
v := r.conf.Verbosity()
|
||||
inParallel := report.RunningInParallel
|
||||
|
||||
//should we completely omit this spec?
|
||||
if report.State.Is(types.SpecStateSkipped) && r.conf.SilenceSkips {
|
||||
return
|
||||
}
|
||||
|
||||
header := r.specDenoter
|
||||
if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) {
|
||||
header = fmt.Sprintf("[%s]", report.LeafNodeType)
|
||||
@@ -262,9 +283,12 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no content to show, jsut emit the header and return
|
||||
// If we have no content to show, just emit the header and return
|
||||
if !reportHasContent {
|
||||
r.emit(r.f(highlightColor + header + "{{/}}"))
|
||||
if r.conf.ForceNewlines {
|
||||
r.emit("\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -283,26 +307,23 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
|
||||
|
||||
//Emit Stdout/Stderr Output
|
||||
if showSeparateStdSection {
|
||||
r.emitBlock("\n")
|
||||
r.emitBlock(r.fi(1, "{{gray}}Captured StdOut/StdErr Output >>{{/}}"))
|
||||
r.emitBlock(r.fi(1, "%s", report.CapturedStdOutErr))
|
||||
r.emitBlock(r.fi(1, "{{gray}}<< Captured StdOut/StdErr Output{{/}}"))
|
||||
r.wrapTextBlock("Captured StdOut/StdErr Output", func() {
|
||||
r.emitBlock(r.fi(1, "%s", report.CapturedStdOutErr))
|
||||
})
|
||||
}
|
||||
|
||||
if showSeparateVisibilityAlwaysReportsSection {
|
||||
r.emitBlock("\n")
|
||||
r.emitBlock(r.fi(1, "{{gray}}Report Entries >>{{/}}"))
|
||||
for _, entry := range report.ReportEntries.WithVisibility(types.ReportEntryVisibilityAlways) {
|
||||
r.emitReportEntry(1, entry)
|
||||
}
|
||||
r.emitBlock(r.fi(1, "{{gray}}<< Report Entries{{/}}"))
|
||||
r.wrapTextBlock("Report Entries", func() {
|
||||
for _, entry := range report.ReportEntries.WithVisibility(types.ReportEntryVisibilityAlways) {
|
||||
r.emitReportEntry(1, entry)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if showTimeline {
|
||||
r.emitBlock("\n")
|
||||
r.emitBlock(r.fi(1, "{{gray}}Timeline >>{{/}}"))
|
||||
r.emitTimeline(1, report, timeline)
|
||||
r.emitBlock(r.fi(1, "{{gray}}<< Timeline{{/}}"))
|
||||
r.wrapTextBlock("Timeline", func() {
|
||||
r.emitTimeline(1, report, timeline)
|
||||
})
|
||||
}
|
||||
|
||||
// Emit Failure Message
|
||||
@@ -405,7 +426,15 @@ func (r *DefaultReporter) emitShortFailure(indent uint, state types.SpecState, f
|
||||
func (r *DefaultReporter) emitFailure(indent uint, state types.SpecState, failure types.Failure, includeAdditionalFailure bool) {
|
||||
highlightColor := r.highlightColorForState(state)
|
||||
r.emitBlock(r.fi(indent, highlightColor+"[%s] %s{{/}}", r.humanReadableState(state), failure.Message))
|
||||
r.emitBlock(r.fi(indent, highlightColor+"In {{bold}}[%s]{{/}}"+highlightColor+" at: {{bold}}%s{{/}} {{gray}}@ %s{{/}}\n", failure.FailureNodeType, failure.Location, failure.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT)))
|
||||
if r.conf.GithubOutput {
|
||||
level := "error"
|
||||
if state.Is(types.SpecStateSkipped) {
|
||||
level = "notice"
|
||||
}
|
||||
r.emitBlock(r.fi(indent, "::%s file=%s,line=%d::%s %s", level, failure.Location.FileName, failure.Location.LineNumber, failure.FailureNodeType, failure.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT)))
|
||||
} else {
|
||||
r.emitBlock(r.fi(indent, highlightColor+"In {{bold}}[%s]{{/}}"+highlightColor+" at: {{bold}}%s{{/}} {{gray}}@ %s{{/}}\n", failure.FailureNodeType, failure.Location, failure.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT)))
|
||||
}
|
||||
if failure.ForwardedPanic != "" {
|
||||
r.emitBlock("\n")
|
||||
r.emitBlock(r.fi(indent, highlightColor+"%s{{/}}", failure.ForwardedPanic))
|
||||
@@ -656,11 +685,11 @@ func (r *DefaultReporter) _emit(s string, block bool, isDelimiter bool) {
|
||||
}
|
||||
|
||||
/* Rendering text */
|
||||
func (r *DefaultReporter) f(format string, args ...interface{}) string {
|
||||
func (r *DefaultReporter) f(format string, args ...any) string {
|
||||
return r.formatter.F(format, args...)
|
||||
}
|
||||
|
||||
func (r *DefaultReporter) fi(indentation uint, format string, args ...interface{}) string {
|
||||
func (r *DefaultReporter) fi(indentation uint, format string, args ...any) string {
|
||||
return r.formatter.Fi(indentation, format, args...)
|
||||
}
|
||||
|
||||
|
||||
6
vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go
generated
vendored
6
vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go
generated
vendored
@@ -18,6 +18,7 @@ func GenerateJSONReport(report types.Report, destination string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
err = enc.Encode([]types.Report{
|
||||
@@ -26,7 +27,7 @@ func GenerateJSONReport(report types.Report, destination string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergeJSONReports produces a single JSON-formatted report at the passed in destination by merging the JSON-formatted reports provided in sources
|
||||
@@ -57,11 +58,12 @@ func MergeAndCleanupJSONReports(sources []string, destination string) ([]string,
|
||||
if err != nil {
|
||||
return messages, err
|
||||
}
|
||||
defer f.Close()
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
err = enc.Encode(allReports)
|
||||
if err != nil {
|
||||
return messages, err
|
||||
}
|
||||
return messages, f.Close()
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
14
vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
generated
vendored
14
vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
generated
vendored
@@ -15,6 +15,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/config"
|
||||
@@ -104,6 +105,8 @@ type JUnitProperty struct {
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
var ownerRE = regexp.MustCompile(`(?i)^owner:(.*)$`)
|
||||
|
||||
type JUnitTestCase struct {
|
||||
// Name maps onto the full text of the spec - equivalent to "[SpecReport.LeafNodeType] SpecReport.FullText()"
|
||||
Name string `xml:"name,attr"`
|
||||
@@ -113,6 +116,8 @@ type JUnitTestCase struct {
|
||||
Status string `xml:"status,attr"`
|
||||
// Time is the time in seconds to execute the spec - maps onto SpecReport.RunTime
|
||||
Time float64 `xml:"time,attr"`
|
||||
// Owner is the owner the spec - is set if a label matching Label("owner:X") is provided. The last matching label is used as the owner, thereby allowing specs to override owners specified in container nodes.
|
||||
Owner string `xml:"owner,attr,omitempty"`
|
||||
//Skipped is populated with a message if the test was skipped or pending
|
||||
Skipped *JUnitSkipped `xml:"skipped,omitempty"`
|
||||
//Error is populated if the test panicked or was interrupted
|
||||
@@ -172,6 +177,7 @@ func GenerateJUnitReportWithConfig(report types.Report, dst string, config Junit
|
||||
{"FocusFiles", strings.Join(report.SuiteConfig.FocusFiles, ";")},
|
||||
{"SkipFiles", strings.Join(report.SuiteConfig.SkipFiles, ";")},
|
||||
{"FailOnPending", fmt.Sprintf("%t", report.SuiteConfig.FailOnPending)},
|
||||
{"FailOnEmpty", fmt.Sprintf("%t", report.SuiteConfig.FailOnEmpty)},
|
||||
{"FailFast", fmt.Sprintf("%t", report.SuiteConfig.FailFast)},
|
||||
{"FlakeAttempts", fmt.Sprintf("%d", report.SuiteConfig.FlakeAttempts)},
|
||||
{"DryRun", fmt.Sprintf("%t", report.SuiteConfig.DryRun)},
|
||||
@@ -195,6 +201,12 @@ func GenerateJUnitReportWithConfig(report types.Report, dst string, config Junit
|
||||
if len(labels) > 0 && !config.OmitSpecLabels {
|
||||
name = name + " [" + strings.Join(labels, ", ") + "]"
|
||||
}
|
||||
owner := ""
|
||||
for _, label := range labels {
|
||||
if matches := ownerRE.FindStringSubmatch(label); len(matches) == 2 {
|
||||
owner = matches[1]
|
||||
}
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
|
||||
test := JUnitTestCase{
|
||||
@@ -202,6 +214,7 @@ func GenerateJUnitReportWithConfig(report types.Report, dst string, config Junit
|
||||
Classname: report.SuiteDescription,
|
||||
Status: spec.State.String(),
|
||||
Time: spec.RunTime.Seconds(),
|
||||
Owner: owner,
|
||||
}
|
||||
if !spec.State.Is(config.OmitTimelinesForSpecState) {
|
||||
test.SystemErr = systemErrForUnstructuredReporters(spec)
|
||||
@@ -312,6 +325,7 @@ func MergeAndCleanupJUnitReports(sources []string, dst string) ([]string, error)
|
||||
continue
|
||||
}
|
||||
err = xml.NewDecoder(f).Decode(&report)
|
||||
_ = f.Close()
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("Could not decode %s:\n%s", source, err.Error()))
|
||||
continue
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/types/code_location.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/types/code_location.go
generated
vendored
@@ -149,7 +149,7 @@ func PruneStack(fullStackTrace string, skip int) string {
|
||||
re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`)
|
||||
for i := 0; i < len(stack)/2; i++ {
|
||||
// We filter out based on the source code file name.
|
||||
if !re.Match([]byte(stack[i*2+1])) {
|
||||
if !re.MatchString(stack[i*2+1]) {
|
||||
prunedStack = append(prunedStack, stack[i*2])
|
||||
prunedStack = append(prunedStack, stack[i*2+1])
|
||||
}
|
||||
|
||||
84
vendor/github.com/onsi/ginkgo/v2/types/config.go
generated
vendored
84
vendor/github.com/onsi/ginkgo/v2/types/config.go
generated
vendored
@@ -25,6 +25,7 @@ type SuiteConfig struct {
|
||||
SkipFiles []string
|
||||
LabelFilter string
|
||||
FailOnPending bool
|
||||
FailOnEmpty bool
|
||||
FailFast bool
|
||||
FlakeAttempts int
|
||||
MustPassRepeatedly int
|
||||
@@ -89,6 +90,9 @@ type ReporterConfig struct {
|
||||
VeryVerbose bool
|
||||
FullTrace bool
|
||||
ShowNodeEvents bool
|
||||
GithubOutput bool
|
||||
SilenceSkips bool
|
||||
ForceNewlines bool
|
||||
|
||||
JSONReport string
|
||||
JUnitReport string
|
||||
@@ -155,7 +159,7 @@ func (g CLIConfig) ComputedProcs() int {
|
||||
|
||||
n := 1
|
||||
if g.Parallel {
|
||||
n = runtime.NumCPU()
|
||||
n = runtime.GOMAXPROCS(-1)
|
||||
if n > 4 {
|
||||
n = n - 1
|
||||
}
|
||||
@@ -168,7 +172,7 @@ func (g CLIConfig) ComputedNumCompilers() int {
|
||||
return g.NumCompilers
|
||||
}
|
||||
|
||||
return runtime.NumCPU()
|
||||
return runtime.GOMAXPROCS(-1)
|
||||
}
|
||||
|
||||
// Configuration for the Ginkgo CLI capturing available go flags
|
||||
@@ -198,6 +202,7 @@ type GoFlagsConfig struct {
|
||||
A bool
|
||||
ASMFlags string
|
||||
BuildMode string
|
||||
BuildVCS bool
|
||||
Compiler string
|
||||
GCCGoFlags string
|
||||
GCFlags string
|
||||
@@ -215,6 +220,7 @@ type GoFlagsConfig struct {
|
||||
ToolExec string
|
||||
Work bool
|
||||
X bool
|
||||
O string
|
||||
}
|
||||
|
||||
func NewDefaultGoFlagsConfig() GoFlagsConfig {
|
||||
@@ -225,6 +231,10 @@ func (g GoFlagsConfig) BinaryMustBePreserved() bool {
|
||||
return g.BlockProfile != "" || g.CPUProfile != "" || g.MemProfile != "" || g.MutexProfile != ""
|
||||
}
|
||||
|
||||
func (g GoFlagsConfig) NeedsSymbols() bool {
|
||||
return g.BinaryMustBePreserved()
|
||||
}
|
||||
|
||||
// Configuration that were deprecated in 2.0
|
||||
type deprecatedConfig struct {
|
||||
DebugParallel bool
|
||||
@@ -251,8 +261,12 @@ var FlagSections = GinkgoFlagSections{
|
||||
{Key: "filter", Style: "{{cyan}}", Heading: "Filtering Tests"},
|
||||
{Key: "failure", Style: "{{red}}", Heading: "Failure Handling"},
|
||||
{Key: "output", Style: "{{magenta}}", Heading: "Controlling Output Formatting"},
|
||||
{Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis"},
|
||||
{Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis"},
|
||||
{Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis",
|
||||
Description: "When generating a cover files, please pass a filename {{bold}}not{{/}} a path. To specify a different directory use {{magenta}}--output-dir{{/}}.",
|
||||
},
|
||||
{Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis",
|
||||
Description: "When generating profile files, please pass filenames {{bold}}not{{/}} a path. Ginkgo will generate a profile file with the given name in the package's directory. To specify a different directory use {{magenta}}--output-dir{{/}}.",
|
||||
},
|
||||
{Key: "debug", Style: "{{blue}}", Heading: "Debugging Tests",
|
||||
Description: "In addition to these flags, Ginkgo supports a few debugging environment variables. To change the parallel server protocol set {{blue}}GINKGO_PARALLEL_PROTOCOL{{/}} to {{bold}}HTTP{{/}}. To avoid pruning callstacks set {{blue}}GINKGO_PRUNE_STACK{{/}} to {{bold}}FALSE{{/}}."},
|
||||
{Key: "watch", Style: "{{light-yellow}}", Heading: "Controlling Ginkgo Watch"},
|
||||
@@ -264,7 +278,7 @@ var FlagSections = GinkgoFlagSections{
|
||||
// SuiteConfigFlags provides flags for the Ginkgo test process, and CLI
|
||||
var SuiteConfigFlags = GinkgoFlags{
|
||||
{KeyPath: "S.RandomSeed", Name: "seed", SectionKey: "order", UsageDefaultValue: "randomly generated by Ginkgo",
|
||||
Usage: "The seed used to randomize the spec suite."},
|
||||
Usage: "The seed used to randomize the spec suite.", AlwaysExport: true},
|
||||
{KeyPath: "S.RandomizeAllSpecs", Name: "randomize-all", SectionKey: "order", DeprecatedName: "randomizeAllSpecs", DeprecatedDocLink: "changed-command-line-flags",
|
||||
Usage: "If set, ginkgo will randomize all specs together. By default, ginkgo only randomizes the top level Describe, Context and When containers."},
|
||||
|
||||
@@ -274,6 +288,8 @@ var SuiteConfigFlags = GinkgoFlags{
|
||||
Usage: "If set, ginkgo will stop running a test suite after a failure occurs."},
|
||||
{KeyPath: "S.FlakeAttempts", Name: "flake-attempts", SectionKey: "failure", UsageDefaultValue: "0 - failed tests are not retried", DeprecatedName: "flakeAttempts", DeprecatedDocLink: "changed-command-line-flags",
|
||||
Usage: "Make up to this many attempts to run each spec. If any of the attempts succeed, the suite will not be failed."},
|
||||
{KeyPath: "S.FailOnEmpty", Name: "fail-on-empty", SectionKey: "failure",
|
||||
Usage: "If set, ginkgo will mark the test suite as failed if no specs are run."},
|
||||
|
||||
{KeyPath: "S.DryRun", Name: "dry-run", SectionKey: "debug", DeprecatedName: "dryRun", DeprecatedDocLink: "changed-command-line-flags",
|
||||
Usage: "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v."},
|
||||
@@ -320,7 +336,7 @@ var ParallelConfigFlags = GinkgoFlags{
|
||||
// ReporterConfigFlags provides flags for the Ginkgo test process, and CLI
|
||||
var ReporterConfigFlags = GinkgoFlags{
|
||||
{KeyPath: "R.NoColor", Name: "no-color", SectionKey: "output", DeprecatedName: "noColor", DeprecatedDocLink: "changed-command-line-flags",
|
||||
Usage: "If set, suppress color output in default reporter."},
|
||||
Usage: "If set, suppress color output in default reporter. You can also set the environment variable GINKGO_NO_COLOR=TRUE"},
|
||||
{KeyPath: "R.Verbose", Name: "v", SectionKey: "output",
|
||||
Usage: "If set, emits more output including GinkgoWriter contents."},
|
||||
{KeyPath: "R.VeryVerbose", Name: "vv", SectionKey: "output",
|
||||
@@ -331,6 +347,12 @@ var ReporterConfigFlags = GinkgoFlags{
|
||||
Usage: "If set, default reporter prints out the full stack trace when a failure occurs"},
|
||||
{KeyPath: "R.ShowNodeEvents", Name: "show-node-events", SectionKey: "output",
|
||||
Usage: "If set, default reporter prints node > Enter and < Exit events when specs fail"},
|
||||
{KeyPath: "R.GithubOutput", Name: "github-output", SectionKey: "output",
|
||||
Usage: "If set, default reporter prints easier to manage output in Github Actions."},
|
||||
{KeyPath: "R.SilenceSkips", Name: "silence-skips", SectionKey: "output",
|
||||
Usage: "If set, default reporter will not print out skipped tests."},
|
||||
{KeyPath: "R.ForceNewlines", Name: "force-newlines", SectionKey: "output",
|
||||
Usage: "If set, default reporter will ensure a newline appears after each test."},
|
||||
|
||||
{KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output",
|
||||
Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."},
|
||||
@@ -351,7 +373,7 @@ var ReporterConfigFlags = GinkgoFlags{
|
||||
func BuildTestSuiteFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig) (GinkgoFlagSet, error) {
|
||||
flags := SuiteConfigFlags.CopyAppend(ParallelConfigFlags...).CopyAppend(ReporterConfigFlags...)
|
||||
flags = flags.WithPrefix("ginkgo")
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"S": suiteConfig,
|
||||
"R": reporterConfig,
|
||||
"D": &deprecatedConfig{},
|
||||
@@ -499,9 +521,9 @@ var GinkgoCLIWatchFlags = GinkgoFlags{
|
||||
// GoBuildFlags provides flags for the Ginkgo CLI build, run, and watch commands that capture go's build-time flags. These are passed to go test -c by the ginkgo CLI
|
||||
var GoBuildFlags = GinkgoFlags{
|
||||
{KeyPath: "Go.Race", Name: "race", SectionKey: "code-and-coverage-analysis",
|
||||
Usage: "enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, linux/ppc64le and linux/arm64 (only for 48-bit VMA)."},
|
||||
Usage: "enable data race detection. Supported on linux/amd64, linux/ppc64le, linux/arm64, linux/s390x, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64."},
|
||||
{KeyPath: "Go.Vet", Name: "vet", UsageArgument: "list", SectionKey: "code-and-coverage-analysis",
|
||||
Usage: `Configure the invocation of "go vet" during "go test" to use the comma-separated list of vet checks. If list is empty, "go test" runs "go vet" with a curated list of checks believed to be always worth addressing. If list is "off", "go test" does not run "go vet" at all. Available checks can be found by running 'go doc cmd/vet'`},
|
||||
Usage: `Configure the invocation of "go vet" during "go test" to use the comma-separated list of vet checks. If list is empty (by explicitly passing --vet=""), "go test" runs "go vet" with a curated list of checks believed to be always worth addressing. If list is "off", "go test" does not run "go vet" at all. Available checks can be found by running 'go doc cmd/vet'`},
|
||||
{KeyPath: "Go.Cover", Name: "cover", SectionKey: "code-and-coverage-analysis",
|
||||
Usage: "Enable coverage analysis. Note that because coverage works by annotating the source code before compilation, compilation and test failures with coverage enabled may report line numbers that don't correspond to the original sources."},
|
||||
{KeyPath: "Go.CoverMode", Name: "covermode", UsageArgument: "set,count,atomic", SectionKey: "code-and-coverage-analysis",
|
||||
@@ -515,6 +537,8 @@ var GoBuildFlags = GinkgoFlags{
|
||||
Usage: "arguments to pass on each go tool asm invocation."},
|
||||
{KeyPath: "Go.BuildMode", Name: "buildmode", UsageArgument: "mode", SectionKey: "go-build",
|
||||
Usage: "build mode to use. See 'go help buildmode' for more."},
|
||||
{KeyPath: "Go.BuildVCS", Name: "buildvcs", SectionKey: "go-build",
|
||||
Usage: "adds version control information."},
|
||||
{KeyPath: "Go.Compiler", Name: "compiler", UsageArgument: "name", SectionKey: "go-build",
|
||||
Usage: "name of compiler to use, as in runtime.Compiler (gccgo or gc)."},
|
||||
{KeyPath: "Go.GCCGoFlags", Name: "gccgoflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build",
|
||||
@@ -549,12 +573,14 @@ var GoBuildFlags = GinkgoFlags{
|
||||
Usage: "print the name of the temporary work directory and do not delete it when exiting."},
|
||||
{KeyPath: "Go.X", Name: "x", SectionKey: "go-build",
|
||||
Usage: "print the commands."},
|
||||
{KeyPath: "Go.O", Name: "o", SectionKey: "go-build",
|
||||
Usage: "output binary path (including name)."},
|
||||
}
|
||||
|
||||
// GoRunFlags provides flags for the Ginkgo CLI run, and watch commands that capture go's run-time flags. These are passed to the compiled test binary by the ginkgo CLI
|
||||
var GoRunFlags = GinkgoFlags{
|
||||
{KeyPath: "Go.CoverProfile", Name: "coverprofile", UsageArgument: "file", SectionKey: "code-and-coverage-analysis",
|
||||
Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover.`},
|
||||
Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover. Must be passed a filename, not a path. Use output-dir to control the location of the output.`},
|
||||
{KeyPath: "Go.BlockProfile", Name: "blockprofile", UsageArgument: "file", SectionKey: "performance-analysis",
|
||||
Usage: `Write a goroutine blocking profile to the specified file when all tests are complete. Preserves test binary.`},
|
||||
{KeyPath: "Go.BlockProfileRate", Name: "blockprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis",
|
||||
@@ -582,6 +608,22 @@ func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsCo
|
||||
errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails())
|
||||
}
|
||||
|
||||
if strings.ContainsRune(goFlagsConfig.CoverProfile, os.PathSeparator) {
|
||||
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--coverprofile", goFlagsConfig.CoverProfile))
|
||||
}
|
||||
if strings.ContainsRune(goFlagsConfig.CPUProfile, os.PathSeparator) {
|
||||
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--cpuprofile", goFlagsConfig.CPUProfile))
|
||||
}
|
||||
if strings.ContainsRune(goFlagsConfig.MemProfile, os.PathSeparator) {
|
||||
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--memprofile", goFlagsConfig.MemProfile))
|
||||
}
|
||||
if strings.ContainsRune(goFlagsConfig.BlockProfile, os.PathSeparator) {
|
||||
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--blockprofile", goFlagsConfig.BlockProfile))
|
||||
}
|
||||
if strings.ContainsRune(goFlagsConfig.MutexProfile, os.PathSeparator) {
|
||||
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--mutexprofile", goFlagsConfig.MutexProfile))
|
||||
}
|
||||
|
||||
//initialize the output directory
|
||||
if cliConfig.OutputDir != "" {
|
||||
err := os.MkdirAll(cliConfig.OutputDir, 0777)
|
||||
@@ -602,7 +644,7 @@ func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsCo
|
||||
}
|
||||
|
||||
// GenerateGoTestCompileArgs is used by the Ginkgo CLI to generate command line arguments to pass to the go test -c command when compiling the test
|
||||
func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string, packageToBuild string, pathToInvocationPath string) ([]string, error) {
|
||||
func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, packageToBuild string, pathToInvocationPath string, preserveSymbols bool) ([]string, error) {
|
||||
// if the user has set the CoverProfile run-time flag make sure to set the build-time cover flag to make sure
|
||||
// the built test binary can generate a coverprofile
|
||||
if goFlagsConfig.CoverProfile != "" {
|
||||
@@ -625,10 +667,14 @@ func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string,
|
||||
goFlagsConfig.CoverPkg = strings.Join(adjustedCoverPkgs, ",")
|
||||
}
|
||||
|
||||
args := []string{"test", "-c", "-o", destination, packageToBuild}
|
||||
if !goFlagsConfig.NeedsSymbols() && goFlagsConfig.LDFlags == "" && !preserveSymbols {
|
||||
goFlagsConfig.LDFlags = "-w -s"
|
||||
}
|
||||
|
||||
args := []string{"test", "-c", packageToBuild}
|
||||
goArgs, err := GenerateFlagArgs(
|
||||
GoBuildFlags,
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Go": &goFlagsConfig,
|
||||
},
|
||||
)
|
||||
@@ -647,7 +693,7 @@ func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterC
|
||||
flags = flags.CopyAppend(ParallelConfigFlags.WithPrefix("ginkgo")...)
|
||||
flags = flags.CopyAppend(ReporterConfigFlags.WithPrefix("ginkgo")...)
|
||||
flags = flags.CopyAppend(GoRunFlags.WithPrefix("test")...)
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"S": &suiteConfig,
|
||||
"R": &reporterConfig,
|
||||
"Go": &goFlagsConfig,
|
||||
@@ -659,7 +705,7 @@ func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterC
|
||||
// GenerateGoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled non-Ginkgo test binary
|
||||
func GenerateGoTestRunArgs(goFlagsConfig GoFlagsConfig) ([]string, error) {
|
||||
flags := GoRunFlags.WithPrefix("test")
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"Go": &goFlagsConfig,
|
||||
}
|
||||
|
||||
@@ -681,7 +727,7 @@ func BuildRunCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterCo
|
||||
flags = flags.CopyAppend(GoBuildFlags...)
|
||||
flags = flags.CopyAppend(GoRunFlags...)
|
||||
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"S": suiteConfig,
|
||||
"R": reporterConfig,
|
||||
"C": cliConfig,
|
||||
@@ -702,7 +748,7 @@ func BuildWatchCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *Reporter
|
||||
flags = flags.CopyAppend(GoBuildFlags...)
|
||||
flags = flags.CopyAppend(GoRunFlags...)
|
||||
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"S": suiteConfig,
|
||||
"R": reporterConfig,
|
||||
"C": cliConfig,
|
||||
@@ -718,7 +764,7 @@ func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig
|
||||
flags := GinkgoCLISharedFlags
|
||||
flags = flags.CopyAppend(GoBuildFlags...)
|
||||
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"C": cliConfig,
|
||||
"Go": goFlagsConfig,
|
||||
"D": &deprecatedConfig{},
|
||||
@@ -742,7 +788,7 @@ func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig
|
||||
func BuildLabelsCommandFlagSet(cliConfig *CLIConfig) (GinkgoFlagSet, error) {
|
||||
flags := GinkgoCLISharedFlags.SubsetWithNames("r", "skip-package")
|
||||
|
||||
bindings := map[string]interface{}{
|
||||
bindings := map[string]any{
|
||||
"C": cliConfig,
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go
generated
vendored
@@ -113,7 +113,7 @@ type DeprecatedSpecFailure struct {
|
||||
|
||||
type DeprecatedSpecMeasurement struct {
|
||||
Name string
|
||||
Info interface{}
|
||||
Info any
|
||||
Order int
|
||||
|
||||
Results []float64
|
||||
|
||||
31
vendor/github.com/onsi/ginkgo/v2/types/errors.go
generated
vendored
31
vendor/github.com/onsi/ginkgo/v2/types/errors.go
generated
vendored
@@ -88,7 +88,7 @@ body of a {{bold}}Describe{{/}}, {{bold}}Context{{/}}, or {{bold}}When{{/}}.`, n
|
||||
}
|
||||
}
|
||||
|
||||
func (g ginkgoErrors) CaughtPanicDuringABuildPhase(caughtPanic interface{}, cl CodeLocation) error {
|
||||
func (g ginkgoErrors) CaughtPanicDuringABuildPhase(caughtPanic any, cl CodeLocation) error {
|
||||
return GinkgoError{
|
||||
Heading: "Assertion or Panic detected during tree construction",
|
||||
Message: formatter.F(
|
||||
@@ -189,7 +189,7 @@ func (g ginkgoErrors) InvalidDeclarationOfFlakeAttemptsAndMustPassRepeatedly(cl
|
||||
}
|
||||
}
|
||||
|
||||
func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator interface{}) error {
|
||||
func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator any) error {
|
||||
return GinkgoError{
|
||||
Heading: "Unknown Decorator",
|
||||
Message: formatter.F(`[%s] node was passed an unknown decorator: '%#v'`, nodeType, decorator),
|
||||
@@ -345,7 +345,7 @@ func (g ginkgoErrors) PushingCleanupInCleanupNode(cl CodeLocation) error {
|
||||
}
|
||||
|
||||
/* ReportEntry errors */
|
||||
func (g ginkgoErrors) TooManyReportEntryValues(cl CodeLocation, arg interface{}) error {
|
||||
func (g ginkgoErrors) TooManyReportEntryValues(cl CodeLocation, arg any) error {
|
||||
return GinkgoError{
|
||||
Heading: "Too Many ReportEntry Values",
|
||||
Message: formatter.F(`{{bold}}AddGinkgoReport{{/}} can only be given one value. Got unexpected value: %#v`, arg),
|
||||
@@ -505,6 +505,15 @@ func (g ginkgoErrors) IncorrectVariadicParameterTypeToTableFunction(expected, ac
|
||||
}
|
||||
}
|
||||
|
||||
func (g ginkgoErrors) ContextsCannotBeUsedInSubtreeTables(cl CodeLocation) error {
|
||||
return GinkgoError{
|
||||
Heading: "Contexts cannot be used in subtree tables",
|
||||
Message: "You''ve defined a subtree body function that accepts a context but did not provide one in the table entry. Ginkgo SpecContexts can only be passed in to subject and setup nodes - so if you are trying to implement a spec timeout you should request a context in the It function within your subtree body function, not in the subtree body function itself.",
|
||||
CodeLocation: cl,
|
||||
DocLink: "table-specs",
|
||||
}
|
||||
}
|
||||
|
||||
/* Parallel Synchronization errors */
|
||||
|
||||
func (g ginkgoErrors) AggregatedReportUnavailableDueToNodeDisappearing() error {
|
||||
@@ -530,7 +539,7 @@ func (g ginkgoErrors) SynchronizedBeforeSuiteDisappearedOnProc1() error {
|
||||
|
||||
/* Configuration errors */
|
||||
|
||||
func (g ginkgoErrors) UnknownTypePassedToRunSpecs(value interface{}) error {
|
||||
func (g ginkgoErrors) UnknownTypePassedToRunSpecs(value any) error {
|
||||
return GinkgoError{
|
||||
Heading: "Unknown Type passed to RunSpecs",
|
||||
Message: fmt.Sprintf("RunSpecs() accepts labels, and configuration of type types.SuiteConfig and/or types.ReporterConfig.\n You passed in: %v", value),
|
||||
@@ -620,6 +629,20 @@ func (g ginkgoErrors) BothRepeatAndUntilItFails() error {
|
||||
}
|
||||
}
|
||||
|
||||
func (g ginkgoErrors) ExpectFilenameNotPath(flag string, path string) error {
|
||||
return GinkgoError{
|
||||
Heading: fmt.Sprintf("%s expects a filename but was given a path: %s", flag, path),
|
||||
Message: fmt.Sprintf("%s takes a filename, not a path. Use --output-dir to specify a directory to collect all test outputs.", flag),
|
||||
}
|
||||
}
|
||||
|
||||
func (g ginkgoErrors) FlagAfterPositionalParameter() error {
|
||||
return GinkgoError{
|
||||
Heading: "Malformed arguments - detected a flag after the package liste",
|
||||
Message: "Make sure all flags appear {{bold}}after{{/}} the Ginkgo subcommand and {{bold}}before{{/}} your list of packages (or './...').\n{{gray}}e.g. 'ginkgo run -p my_package' is valid but `ginkgo -p run my_package` is not.\n{{gray}}e.g. 'ginkgo -p -vet=\"\" ./...' is valid but 'ginkgo -p ./... -vet=\"\"' is not{{/}}",
|
||||
}
|
||||
}
|
||||
|
||||
/* Stack-Trace parsing errors */
|
||||
|
||||
func (g ginkgoErrors) FailedToParseStackTrace(message string) error {
|
||||
|
||||
25
vendor/github.com/onsi/ginkgo/v2/types/flags.go
generated
vendored
25
vendor/github.com/onsi/ginkgo/v2/types/flags.go
generated
vendored
@@ -24,7 +24,8 @@ type GinkgoFlag struct {
|
||||
DeprecatedDocLink string
|
||||
DeprecatedVersion string
|
||||
|
||||
ExportAs string
|
||||
ExportAs string
|
||||
AlwaysExport bool
|
||||
}
|
||||
|
||||
type GinkgoFlags []GinkgoFlag
|
||||
@@ -91,7 +92,7 @@ func (gfs GinkgoFlagSections) Lookup(key string) (GinkgoFlagSection, bool) {
|
||||
|
||||
type GinkgoFlagSet struct {
|
||||
flags GinkgoFlags
|
||||
bindings interface{}
|
||||
bindings any
|
||||
|
||||
sections GinkgoFlagSections
|
||||
extraGoFlagsSection GinkgoFlagSection
|
||||
@@ -100,7 +101,7 @@ type GinkgoFlagSet struct {
|
||||
}
|
||||
|
||||
// Call NewGinkgoFlagSet to create GinkgoFlagSet that creates and binds to it's own *flag.FlagSet
|
||||
func NewGinkgoFlagSet(flags GinkgoFlags, bindings interface{}, sections GinkgoFlagSections) (GinkgoFlagSet, error) {
|
||||
func NewGinkgoFlagSet(flags GinkgoFlags, bindings any, sections GinkgoFlagSections) (GinkgoFlagSet, error) {
|
||||
return bindFlagSet(GinkgoFlagSet{
|
||||
flags: flags,
|
||||
bindings: bindings,
|
||||
@@ -109,7 +110,7 @@ func NewGinkgoFlagSet(flags GinkgoFlags, bindings interface{}, sections GinkgoFl
|
||||
}
|
||||
|
||||
// Call NewGinkgoFlagSet to create GinkgoFlagSet that extends an existing *flag.FlagSet
|
||||
func NewAttachedGinkgoFlagSet(flagSet *flag.FlagSet, flags GinkgoFlags, bindings interface{}, sections GinkgoFlagSections, extraGoFlagsSection GinkgoFlagSection) (GinkgoFlagSet, error) {
|
||||
func NewAttachedGinkgoFlagSet(flagSet *flag.FlagSet, flags GinkgoFlags, bindings any, sections GinkgoFlagSections, extraGoFlagsSection GinkgoFlagSection) (GinkgoFlagSet, error) {
|
||||
return bindFlagSet(GinkgoFlagSet{
|
||||
flags: flags,
|
||||
bindings: bindings,
|
||||
@@ -334,7 +335,7 @@ func (f GinkgoFlagSet) substituteUsage() {
|
||||
fmt.Fprintln(f.flagSet.Output(), f.Usage())
|
||||
}
|
||||
|
||||
func valueAtKeyPath(root interface{}, keyPath string) (reflect.Value, bool) {
|
||||
func valueAtKeyPath(root any, keyPath string) (reflect.Value, bool) {
|
||||
if len(keyPath) == 0 {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
@@ -431,8 +432,8 @@ func (ssv stringSliceVar) Set(s string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//given a set of GinkgoFlags and bindings, generate flag arguments suitable to be passed to an application with that set of flags configured.
|
||||
func GenerateFlagArgs(flags GinkgoFlags, bindings interface{}) ([]string, error) {
|
||||
// given a set of GinkgoFlags and bindings, generate flag arguments suitable to be passed to an application with that set of flags configured.
|
||||
func GenerateFlagArgs(flags GinkgoFlags, bindings any) ([]string, error) {
|
||||
result := []string{}
|
||||
for _, flag := range flags {
|
||||
name := flag.ExportAs
|
||||
@@ -451,19 +452,19 @@ func GenerateFlagArgs(flags GinkgoFlags, bindings interface{}) ([]string, error)
|
||||
iface := value.Interface()
|
||||
switch value.Type() {
|
||||
case reflect.TypeOf(string("")):
|
||||
if iface.(string) != "" {
|
||||
if iface.(string) != "" || flag.AlwaysExport {
|
||||
result = append(result, fmt.Sprintf("--%s=%s", name, iface))
|
||||
}
|
||||
case reflect.TypeOf(int64(0)):
|
||||
if iface.(int64) != 0 {
|
||||
if iface.(int64) != 0 || flag.AlwaysExport {
|
||||
result = append(result, fmt.Sprintf("--%s=%d", name, iface))
|
||||
}
|
||||
case reflect.TypeOf(float64(0)):
|
||||
if iface.(float64) != 0 {
|
||||
if iface.(float64) != 0 || flag.AlwaysExport {
|
||||
result = append(result, fmt.Sprintf("--%s=%f", name, iface))
|
||||
}
|
||||
case reflect.TypeOf(int(0)):
|
||||
if iface.(int) != 0 {
|
||||
if iface.(int) != 0 || flag.AlwaysExport {
|
||||
result = append(result, fmt.Sprintf("--%s=%d", name, iface))
|
||||
}
|
||||
case reflect.TypeOf(bool(true)):
|
||||
@@ -471,7 +472,7 @@ func GenerateFlagArgs(flags GinkgoFlags, bindings interface{}) ([]string, error)
|
||||
result = append(result, fmt.Sprintf("--%s", name))
|
||||
}
|
||||
case reflect.TypeOf(time.Duration(0)):
|
||||
if iface.(time.Duration) != time.Duration(0) {
|
||||
if iface.(time.Duration) != time.Duration(0) || flag.AlwaysExport {
|
||||
result = append(result, fmt.Sprintf("--%s=%s", name, iface))
|
||||
}
|
||||
|
||||
|
||||
231
vendor/github.com/onsi/ginkgo/v2/types/label_filter.go
generated
vendored
231
vendor/github.com/onsi/ginkgo/v2/types/label_filter.go
generated
vendored
@@ -45,6 +45,83 @@ func orAction(a, b LabelFilter) LabelFilter {
|
||||
return func(labels []string) bool { return a(labels) || b(labels) }
|
||||
}
|
||||
|
||||
func labelSetFor(key string, labels []string) map[string]bool {
|
||||
key = strings.ToLower(strings.TrimSpace(key))
|
||||
out := map[string]bool{}
|
||||
for _, label := range labels {
|
||||
components := strings.SplitN(label, ":", 2)
|
||||
if len(components) < 2 {
|
||||
continue
|
||||
}
|
||||
if key == strings.ToLower(strings.TrimSpace(components[0])) {
|
||||
out[strings.ToLower(strings.TrimSpace(components[1]))] = true
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func isEmptyLabelSetAction(key string) LabelFilter {
|
||||
return func(labels []string) bool {
|
||||
return len(labelSetFor(key, labels)) == 0
|
||||
}
|
||||
}
|
||||
|
||||
func containsAnyLabelSetAction(key string, expectedValues []string) LabelFilter {
|
||||
return func(labels []string) bool {
|
||||
set := labelSetFor(key, labels)
|
||||
for _, value := range expectedValues {
|
||||
if set[value] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func containsAllLabelSetAction(key string, expectedValues []string) LabelFilter {
|
||||
return func(labels []string) bool {
|
||||
set := labelSetFor(key, labels)
|
||||
for _, value := range expectedValues {
|
||||
if !set[value] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func consistsOfLabelSetAction(key string, expectedValues []string) LabelFilter {
|
||||
return func(labels []string) bool {
|
||||
set := labelSetFor(key, labels)
|
||||
if len(set) != len(expectedValues) {
|
||||
return false
|
||||
}
|
||||
for _, value := range expectedValues {
|
||||
if !set[value] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func isSubsetOfLabelSetAction(key string, expectedValues []string) LabelFilter {
|
||||
expectedSet := map[string]bool{}
|
||||
for _, value := range expectedValues {
|
||||
expectedSet[value] = true
|
||||
}
|
||||
return func(labels []string) bool {
|
||||
set := labelSetFor(key, labels)
|
||||
for value := range set {
|
||||
if !expectedSet[value] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
type lfToken uint
|
||||
|
||||
const (
|
||||
@@ -58,6 +135,9 @@ const (
|
||||
lfTokenOr
|
||||
lfTokenRegexp
|
||||
lfTokenLabel
|
||||
lfTokenSetKey
|
||||
lfTokenSetOperation
|
||||
lfTokenSetArgument
|
||||
lfTokenEOF
|
||||
)
|
||||
|
||||
@@ -71,6 +151,8 @@ func (l lfToken) Precedence() int {
|
||||
return 2
|
||||
case lfTokenNot:
|
||||
return 3
|
||||
case lfTokenSetOperation:
|
||||
return 4
|
||||
}
|
||||
return -1
|
||||
}
|
||||
@@ -93,6 +175,12 @@ func (l lfToken) String() string {
|
||||
return "/regexp/"
|
||||
case lfTokenLabel:
|
||||
return "label"
|
||||
case lfTokenSetKey:
|
||||
return "set_key"
|
||||
case lfTokenSetOperation:
|
||||
return "set_operation"
|
||||
case lfTokenSetArgument:
|
||||
return "set_argument"
|
||||
case lfTokenEOF:
|
||||
return "EOF"
|
||||
}
|
||||
@@ -148,6 +236,35 @@ func (tn *treeNode) constructLabelFilter(input string) (LabelFilter, error) {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("RegExp compilation error: %s", err))
|
||||
}
|
||||
return matchLabelRegexAction(re), nil
|
||||
case lfTokenSetOperation:
|
||||
tokenSetOperation := strings.ToLower(tn.value)
|
||||
if tokenSetOperation == "isempty" {
|
||||
return isEmptyLabelSetAction(tn.leftNode.value), nil
|
||||
}
|
||||
if tn.rightNode == nil {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("Set operation '%s' is missing an argument.", tn.value))
|
||||
}
|
||||
|
||||
rawValues := strings.Split(tn.rightNode.value, ",")
|
||||
values := make([]string, len(rawValues))
|
||||
for i := range rawValues {
|
||||
values[i] = strings.ToLower(strings.TrimSpace(rawValues[i]))
|
||||
if strings.ContainsAny(values[i], "&|!,()/") {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.rightNode.location, fmt.Sprintf("Invalid label value '%s' in set operation argument.", values[i]))
|
||||
} else if values[i] == "" {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.rightNode.location, "Empty label value in set operation argument.")
|
||||
}
|
||||
}
|
||||
switch tokenSetOperation {
|
||||
case "containsany":
|
||||
return containsAnyLabelSetAction(tn.leftNode.value, values), nil
|
||||
case "containsall":
|
||||
return containsAllLabelSetAction(tn.leftNode.value, values), nil
|
||||
case "consistsof":
|
||||
return consistsOfLabelSetAction(tn.leftNode.value, values), nil
|
||||
case "issubsetof":
|
||||
return isSubsetOfLabelSetAction(tn.leftNode.value, values), nil
|
||||
}
|
||||
}
|
||||
|
||||
if tn.rightNode == nil {
|
||||
@@ -203,7 +320,17 @@ func (tn *treeNode) toString(indent int) string {
|
||||
return out
|
||||
}
|
||||
|
||||
var validSetOperations = map[string]string{
|
||||
"containsany": "containsAny",
|
||||
"containsall": "containsAll",
|
||||
"consistsof": "consistsOf",
|
||||
"issubsetof": "isSubsetOf",
|
||||
"isempty": "isEmpty",
|
||||
}
|
||||
|
||||
func tokenize(input string) func() (*treeNode, error) {
|
||||
lastToken := lfTokenInvalid
|
||||
lastValue := ""
|
||||
runes, i := []rune(input), 0
|
||||
|
||||
peekIs := func(r rune) bool {
|
||||
@@ -216,7 +343,7 @@ func tokenize(input string) func() (*treeNode, error) {
|
||||
consumeUntil := func(cutset string) (string, int) {
|
||||
j := i
|
||||
for ; j < len(runes); j++ {
|
||||
if strings.IndexRune(cutset, runes[j]) >= 0 {
|
||||
if strings.ContainsRune(cutset, runes[j]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -233,6 +360,53 @@ func tokenize(input string) func() (*treeNode, error) {
|
||||
}
|
||||
|
||||
node := &treeNode{location: i}
|
||||
defer func() {
|
||||
lastToken = node.token
|
||||
lastValue = node.value
|
||||
}()
|
||||
|
||||
if lastToken == lfTokenSetKey {
|
||||
//we should get a valid set operation next
|
||||
value, n := consumeUntil(" )")
|
||||
if validSetOperations[strings.ToLower(value)] == "" {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, fmt.Sprintf("Invalid set operation '%s'.", value))
|
||||
}
|
||||
i += n
|
||||
node.token, node.value = lfTokenSetOperation, value
|
||||
return node, nil
|
||||
}
|
||||
if lastToken == lfTokenSetOperation {
|
||||
//we should get an argument next, if we aren't isempty
|
||||
var arg = ""
|
||||
origI := i
|
||||
if runes[i] == '{' {
|
||||
i += 1
|
||||
value, n := consumeUntil("}")
|
||||
if i+n >= len(runes) {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i-1, "Missing closing '}' in set operation argument?")
|
||||
}
|
||||
i += n + 1
|
||||
arg = value
|
||||
} else {
|
||||
value, n := consumeUntil("&|!,()/")
|
||||
i += n
|
||||
arg = strings.TrimSpace(value)
|
||||
}
|
||||
if strings.ToLower(lastValue) == "isempty" && arg != "" {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, origI, fmt.Sprintf("isEmpty does not take arguments, was passed '%s'.", arg))
|
||||
}
|
||||
if arg == "" && strings.ToLower(lastValue) != "isempty" {
|
||||
if i < len(runes) && runes[i] == '/' {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, origI, "Set operations do not support regular expressions.")
|
||||
} else {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, origI, fmt.Sprintf("Set operation '%s' requires an argument.", lastValue))
|
||||
}
|
||||
}
|
||||
// note that we sent an empty SetArgument token if we are isempty
|
||||
node.token, node.value = lfTokenSetArgument, arg
|
||||
return node, nil
|
||||
}
|
||||
|
||||
switch runes[i] {
|
||||
case '&':
|
||||
if !peekIs('&') {
|
||||
@@ -264,8 +438,38 @@ func tokenize(input string) func() (*treeNode, error) {
|
||||
i += n + 1
|
||||
node.token, node.value = lfTokenRegexp, value
|
||||
default:
|
||||
value, n := consumeUntil("&|!,()/")
|
||||
value, n := consumeUntil("&|!,()/:")
|
||||
i += n
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
//are we the beginning of a set operation?
|
||||
if i < len(runes) && runes[i] == ':' {
|
||||
if peekIs(' ') {
|
||||
if value == "" {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Missing set key.")
|
||||
}
|
||||
i += 1
|
||||
//we are the beginning of a set operation
|
||||
node.token, node.value = lfTokenSetKey, value
|
||||
return node, nil
|
||||
}
|
||||
additionalValue, n := consumeUntil("&|!,()/")
|
||||
additionalValue = strings.TrimSpace(additionalValue)
|
||||
if additionalValue == ":" {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Missing set operation.")
|
||||
}
|
||||
i += n
|
||||
value += additionalValue
|
||||
}
|
||||
|
||||
valueToCheckForSetOperation := strings.ToLower(value)
|
||||
for setOperation := range validSetOperations {
|
||||
idx := strings.Index(valueToCheckForSetOperation, " "+setOperation)
|
||||
if idx > 0 {
|
||||
return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i-n+idx+1, fmt.Sprintf("Looks like you are using the set operator '%s' but did not provide a set key. Did you forget the ':'?", validSetOperations[setOperation]))
|
||||
}
|
||||
}
|
||||
|
||||
node.token, node.value = lfTokenLabel, strings.TrimSpace(value)
|
||||
}
|
||||
return node, nil
|
||||
@@ -307,7 +511,7 @@ LOOP:
|
||||
switch node.token {
|
||||
case lfTokenEOF:
|
||||
break LOOP
|
||||
case lfTokenLabel, lfTokenRegexp:
|
||||
case lfTokenLabel, lfTokenRegexp, lfTokenSetKey:
|
||||
if current.rightNode != nil {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Found two adjacent labels. You need an operator between them.")
|
||||
}
|
||||
@@ -326,6 +530,18 @@ LOOP:
|
||||
node.setLeftNode(nodeToStealFrom.rightNode)
|
||||
nodeToStealFrom.setRightNode(node)
|
||||
current = node
|
||||
case lfTokenSetOperation:
|
||||
if current.rightNode == nil {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Set operation '%s' missing left hand operand.", node.value))
|
||||
}
|
||||
node.setLeftNode(current.rightNode)
|
||||
current.setRightNode(node)
|
||||
current = node
|
||||
case lfTokenSetArgument:
|
||||
if current.rightNode != nil {
|
||||
return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Unexpected set argument '%s'.", node.token))
|
||||
}
|
||||
current.setRightNode(node)
|
||||
case lfTokenCloseGroup:
|
||||
firstUnmatchedOpenNode := current.firstUnmatchedOpenNode()
|
||||
if firstUnmatchedOpenNode == nil {
|
||||
@@ -354,5 +570,14 @@ func ValidateAndCleanupLabel(label string, cl CodeLocation) (string, error) {
|
||||
if strings.ContainsAny(out, "&|!,()/") {
|
||||
return "", GinkgoErrors.InvalidLabel(label, cl)
|
||||
}
|
||||
if out[0] == ':' {
|
||||
return "", GinkgoErrors.InvalidLabel(label, cl)
|
||||
}
|
||||
if strings.Contains(out, ":") {
|
||||
components := strings.SplitN(out, ":", 2)
|
||||
if len(components) < 2 || components[1] == "" {
|
||||
return "", GinkgoErrors.InvalidLabel(label, cl)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
8
vendor/github.com/onsi/ginkgo/v2/types/report_entry.go
generated
vendored
8
vendor/github.com/onsi/ginkgo/v2/types/report_entry.go
generated
vendored
@@ -9,18 +9,18 @@ import (
|
||||
// ReportEntryValue wraps a report entry's value ensuring it can be encoded and decoded safely into reports
|
||||
// and across the network connection when running in parallel
|
||||
type ReportEntryValue struct {
|
||||
raw interface{} //unexported to prevent gob from freaking out about unregistered structs
|
||||
raw any //unexported to prevent gob from freaking out about unregistered structs
|
||||
AsJSON string
|
||||
Representation string
|
||||
}
|
||||
|
||||
func WrapEntryValue(value interface{}) ReportEntryValue {
|
||||
func WrapEntryValue(value any) ReportEntryValue {
|
||||
return ReportEntryValue{
|
||||
raw: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (rev ReportEntryValue) GetRawValue() interface{} {
|
||||
func (rev ReportEntryValue) GetRawValue() any {
|
||||
return rev.raw
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ func (entry ReportEntry) StringRepresentation() string {
|
||||
// If used from a rehydrated JSON file _or_ in a ReportAfterSuite when running in parallel this will be
|
||||
// a JSON-decoded {}interface. If you want to reconstitute your original object you can decode the entry.Value.AsJSON
|
||||
// field yourself.
|
||||
func (entry ReportEntry) GetRawValue() interface{} {
|
||||
func (entry ReportEntry) GetRawValue() any {
|
||||
return entry.Value.GetRawValue()
|
||||
}
|
||||
|
||||
|
||||
10
vendor/github.com/onsi/ginkgo/v2/types/types.go
generated
vendored
10
vendor/github.com/onsi/ginkgo/v2/types/types.go
generated
vendored
@@ -3,13 +3,21 @@ package types
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const GINKGO_FOCUS_EXIT_CODE = 197
|
||||
const GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999"
|
||||
|
||||
var GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999"
|
||||
|
||||
func init() {
|
||||
if os.Getenv("GINKGO_TIME_FORMAT") != "" {
|
||||
GINKGO_TIME_FORMAT = os.Getenv("GINKGO_TIME_FORMAT")
|
||||
}
|
||||
}
|
||||
|
||||
// Report captures information about a Ginkgo test run
|
||||
type Report struct {
|
||||
|
||||
2
vendor/github.com/onsi/ginkgo/v2/types/version.go
generated
vendored
2
vendor/github.com/onsi/ginkgo/v2/types/version.go
generated
vendored
@@ -1,3 +1,3 @@
|
||||
package types
|
||||
|
||||
const VERSION = "2.13.0"
|
||||
const VERSION = "2.23.4"
|
||||
|
||||
1
vendor/github.com/quic-go/quic-go/.gitignore
generated
vendored
1
vendor/github.com/quic-go/quic-go/.gitignore
generated
vendored
@@ -4,6 +4,7 @@ main
|
||||
mockgen_tmp.go
|
||||
*.qtr
|
||||
*.qlog
|
||||
*.sqlog
|
||||
*.txt
|
||||
race.[0-9]*
|
||||
|
||||
|
||||
104
vendor/github.com/quic-go/quic-go/.golangci.yml
generated
vendored
104
vendor/github.com/quic-go/quic-go/.golangci.yml
generated
vendored
@@ -1,45 +1,85 @@
|
||||
linters-settings:
|
||||
misspell:
|
||||
ignore-words:
|
||||
- ect
|
||||
depguard:
|
||||
rules:
|
||||
quicvarint:
|
||||
list-mode: strict
|
||||
files:
|
||||
- "**/github.com/quic-go/quic-go/quicvarint/*"
|
||||
- "!$test"
|
||||
allow:
|
||||
- $gostd
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- asciicheck
|
||||
- copyloopvar
|
||||
- depguard
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- goimports
|
||||
- gofmt # redundant, since gofmt *should* be a no-op after gofumpt
|
||||
- gofumpt
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- prealloc
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
|
||||
issues:
|
||||
exclude-files:
|
||||
- internal/handshake/cipher_suite.go
|
||||
exclude-rules:
|
||||
- path: internal/qtls
|
||||
linters:
|
||||
- depguard
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- exhaustive
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
random:
|
||||
deny:
|
||||
- pkg: "math/rand$"
|
||||
desc: use math/rand/v2
|
||||
- pkg: "golang.org/x/exp/rand"
|
||||
desc: use math/rand/v2
|
||||
quicvarint:
|
||||
list-mode: strict
|
||||
files:
|
||||
- '**/github.com/quic-go/quic-go/quicvarint/*'
|
||||
- '!$test'
|
||||
allow:
|
||||
- $gostd
|
||||
rsa:
|
||||
list-mode: original
|
||||
deny:
|
||||
- pkg: crypto/rsa
|
||||
desc: "use crypto/ed25519 instead"
|
||||
misspell:
|
||||
ignore-rules:
|
||||
- ect
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- depguard
|
||||
path: internal/qtls
|
||||
- linters:
|
||||
- exhaustive
|
||||
- prealloc
|
||||
- unparam
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- staticcheck
|
||||
path: _test\.go
|
||||
text: 'SA1029:' # inappropriate key in call to context.WithValue
|
||||
# WebTransport still relies on the ConnectionTracingID and ConnectionTracingKey.
|
||||
# See https://github.com/quic-go/quic-go/issues/4405 for more details.
|
||||
- linters:
|
||||
- staticcheck
|
||||
paths:
|
||||
- http3/
|
||||
- integrationtests/self/http_test.go
|
||||
text: 'SA1019:.+quic\.ConnectionTracing(ID|Key)'
|
||||
paths:
|
||||
- internal/handshake/cipher_suite.go
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- internal/handshake/cipher_suite.go
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
4
vendor/github.com/quic-go/quic-go/README.md
generated
vendored
4
vendor/github.com/quic-go/quic-go/README.md
generated
vendored
@@ -9,7 +9,8 @@
|
||||
|
||||
quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go. It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) and HTTP Datagrams ([RFC 9297](https://datatracker.ietf.org/doc/html/rfc9297)).
|
||||
|
||||
In addition to these base RFCs, it also implements the following RFCs:
|
||||
In addition to these base RFCs, it also implements the following RFCs:
|
||||
|
||||
* Unreliable Datagram Extension ([RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221))
|
||||
* Datagram Packetization Layer Path MTU Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899))
|
||||
* QUIC Version 2 ([RFC 9369](https://datatracker.ietf.org/doc/html/rfc9369))
|
||||
@@ -33,6 +34,7 @@ Detailed documentation can be found on [quic-go.net](https://quic-go.net/docs/).
|
||||
| [Hysteria](https://github.com/apernet/hysteria) | A powerful, lightning fast and censorship resistant proxy |  |
|
||||
| [Mercure](https://github.com/dunglas/mercure) | An open, easy, fast, reliable and battery-efficient solution for real-time communications |  |
|
||||
| [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. |  |
|
||||
| [reverst](https://github.com/flipt-io/reverst) | Reverse Tunnels in Go over HTTP/3 and QUIC |  |
|
||||
| [RoadRunner](https://github.com/roadrunner-server/roadrunner) | High-performance PHP application server, process manager written in Go and powered with plugins |  |
|
||||
| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization |  |
|
||||
| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy |  |
|
||||
|
||||
157
vendor/github.com/quic-go/quic-go/client.go
generated
vendored
157
vendor/github.com/quic-go/quic-go/client.go
generated
vendored
@@ -7,45 +7,15 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/utils"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
sendConn sendConn
|
||||
|
||||
use0RTT bool
|
||||
|
||||
packetHandlers packetHandlerManager
|
||||
onClose func()
|
||||
|
||||
tlsConf *tls.Config
|
||||
config *Config
|
||||
|
||||
connIDGenerator ConnectionIDGenerator
|
||||
srcConnID protocol.ConnectionID
|
||||
destConnID protocol.ConnectionID
|
||||
|
||||
initialPacketNumber protocol.PacketNumber
|
||||
hasNegotiatedVersion bool
|
||||
version protocol.Version
|
||||
|
||||
handshakeChan chan struct{}
|
||||
|
||||
conn quicConn
|
||||
|
||||
tracer *logging.ConnectionTracer
|
||||
tracingID ConnectionTracingID
|
||||
logger utils.Logger
|
||||
}
|
||||
|
||||
// make it possible to mock connection ID for initial generation in the tests
|
||||
var generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial
|
||||
|
||||
// DialAddr establishes a new QUIC connection to a server.
|
||||
// It resolves the address, and then creates a new UDP connection to dial the QUIC server.
|
||||
// When the QUIC connection is closed, this UDP connection is closed.
|
||||
// See Dial for more details.
|
||||
// See [Dial] for more details.
|
||||
func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (Connection, error) {
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||
if err != nil {
|
||||
@@ -63,7 +33,7 @@ func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Confi
|
||||
}
|
||||
|
||||
// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
|
||||
// See DialAddr for more details.
|
||||
// See [DialAddr] for more details.
|
||||
func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) {
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||
if err != nil {
|
||||
@@ -86,7 +56,7 @@ func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *
|
||||
}
|
||||
|
||||
// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn.
|
||||
// See Dial for more details.
|
||||
// See [Dial] for more details.
|
||||
func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) {
|
||||
dl, err := setupTransport(c, tlsConf, false)
|
||||
if err != nil {
|
||||
@@ -101,12 +71,12 @@ func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tl
|
||||
}
|
||||
|
||||
// Dial establishes a new QUIC connection to a server using a net.PacketConn.
|
||||
// If the PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn does),
|
||||
// If the PacketConn satisfies the [OOBCapablePacketConn] interface (as a [net.UDPConn] does),
|
||||
// ECN and packet info support will be enabled. In this case, ReadMsgUDP and WriteMsgUDP
|
||||
// will be used instead of ReadFrom and WriteTo to read/write packets.
|
||||
// The tls.Config must define an application protocol (using NextProtos).
|
||||
//
|
||||
// This is a convenience function. More advanced use cases should instantiate a Transport,
|
||||
// This is a convenience function. More advanced use cases should instantiate a [Transport],
|
||||
// which offers configuration options for a more fine-grained control of the connection establishment,
|
||||
// including reusing the underlying UDP socket for multiple QUIC connections.
|
||||
func Dial(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) {
|
||||
@@ -132,120 +102,3 @@ func setupTransport(c net.PacketConn, tlsConf *tls.Config, createdPacketConn boo
|
||||
isSingleUse: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func dial(
|
||||
ctx context.Context,
|
||||
conn sendConn,
|
||||
connIDGenerator ConnectionIDGenerator,
|
||||
packetHandlers packetHandlerManager,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
onClose func(),
|
||||
use0RTT bool,
|
||||
) (quicConn, error) {
|
||||
c, err := newClient(conn, connIDGenerator, config, tlsConf, onClose, use0RTT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.packetHandlers = packetHandlers
|
||||
|
||||
c.tracingID = nextConnTracingID()
|
||||
if c.config.Tracer != nil {
|
||||
c.tracer = c.config.Tracer(context.WithValue(ctx, ConnectionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID)
|
||||
}
|
||||
if c.tracer != nil && c.tracer.StartedConnection != nil {
|
||||
c.tracer.StartedConnection(c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID)
|
||||
}
|
||||
if err := c.dial(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.conn, nil
|
||||
}
|
||||
|
||||
func newClient(sendConn sendConn, connIDGenerator ConnectionIDGenerator, config *Config, tlsConf *tls.Config, onClose func(), use0RTT bool) (*client, error) {
|
||||
srcConnID, err := connIDGenerator.GenerateConnectionID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destConnID, err := generateConnectionIDForInitial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &client{
|
||||
connIDGenerator: connIDGenerator,
|
||||
srcConnID: srcConnID,
|
||||
destConnID: destConnID,
|
||||
sendConn: sendConn,
|
||||
use0RTT: use0RTT,
|
||||
onClose: onClose,
|
||||
tlsConf: tlsConf,
|
||||
config: config,
|
||||
version: config.Versions[0],
|
||||
handshakeChan: make(chan struct{}),
|
||||
logger: utils.DefaultLogger.WithPrefix("client"),
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *client) dial(ctx context.Context) error {
|
||||
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
||||
|
||||
c.conn = newClientConnection(
|
||||
context.WithValue(context.WithoutCancel(ctx), ConnectionTracingKey, c.tracingID),
|
||||
c.sendConn,
|
||||
c.packetHandlers,
|
||||
c.destConnID,
|
||||
c.srcConnID,
|
||||
c.connIDGenerator,
|
||||
c.config,
|
||||
c.tlsConf,
|
||||
c.initialPacketNumber,
|
||||
c.use0RTT,
|
||||
c.hasNegotiatedVersion,
|
||||
c.tracer,
|
||||
c.logger,
|
||||
c.version,
|
||||
)
|
||||
c.packetHandlers.Add(c.srcConnID, c.conn)
|
||||
|
||||
errorChan := make(chan error, 1)
|
||||
recreateChan := make(chan errCloseForRecreating)
|
||||
go func() {
|
||||
err := c.conn.run()
|
||||
var recreateErr *errCloseForRecreating
|
||||
if errors.As(err, &recreateErr) {
|
||||
recreateChan <- *recreateErr
|
||||
return
|
||||
}
|
||||
if c.onClose != nil {
|
||||
c.onClose()
|
||||
}
|
||||
errorChan <- err // returns as soon as the connection is closed
|
||||
}()
|
||||
|
||||
// only set when we're using 0-RTT
|
||||
// Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever.
|
||||
var earlyConnChan <-chan struct{}
|
||||
if c.use0RTT {
|
||||
earlyConnChan = c.conn.earlyConnReady()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.conn.destroy(nil)
|
||||
return context.Cause(ctx)
|
||||
case err := <-errorChan:
|
||||
return err
|
||||
case recreateErr := <-recreateChan:
|
||||
c.initialPacketNumber = recreateErr.nextPacketNumber
|
||||
c.version = recreateErr.nextVersion
|
||||
c.hasNegotiatedVersion = true
|
||||
return c.dial(ctx)
|
||||
case <-earlyConnChan:
|
||||
// ready to send 0-RTT data
|
||||
return nil
|
||||
case <-c.conn.HandshakeComplete():
|
||||
// handshake successfully completed
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
9
vendor/github.com/quic-go/quic-go/closed_conn.go
generated
vendored
9
vendor/github.com/quic-go/quic-go/closed_conn.go
generated
vendored
@@ -3,6 +3,7 @@ package quic
|
||||
import (
|
||||
"math/bits"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/utils"
|
||||
)
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
// When receiving packets for such a connection, we need to retransmit the packet containing the CONNECTION_CLOSE frame,
|
||||
// with an exponential backoff.
|
||||
type closedLocalConn struct {
|
||||
counter uint32
|
||||
counter atomic.Uint32
|
||||
logger utils.Logger
|
||||
|
||||
sendPacket func(net.Addr, packetInfo)
|
||||
@@ -28,13 +29,13 @@ func newClosedLocalConn(sendPacket func(net.Addr, packetInfo), logger utils.Logg
|
||||
}
|
||||
|
||||
func (c *closedLocalConn) handlePacket(p receivedPacket) {
|
||||
c.counter++
|
||||
n := c.counter.Add(1)
|
||||
// exponential backoff
|
||||
// only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving
|
||||
if bits.OnesCount32(c.counter) != 1 {
|
||||
if bits.OnesCount32(n) != 1 {
|
||||
return
|
||||
}
|
||||
c.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", c.counter)
|
||||
c.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", n)
|
||||
c.sendPacket(p.remoteAddr, p.info)
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/quic-go/quic-go/codecov.yml
generated
vendored
2
vendor/github.com/quic-go/quic-go/codecov.yml
generated
vendored
@@ -6,6 +6,8 @@ coverage:
|
||||
- internal/handshake/cipher_suite.go
|
||||
- internal/utils/linkedlist/linkedlist.go
|
||||
- internal/testdata
|
||||
- logging/connection_tracer_multiplexer.go
|
||||
- logging/tracer_multiplexer.go
|
||||
- testutils/
|
||||
- fuzzing/
|
||||
- metrics/
|
||||
|
||||
2
vendor/github.com/quic-go/quic-go/config.go
generated
vendored
2
vendor/github.com/quic-go/quic-go/config.go
generated
vendored
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/quic-go/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// Clone clones a Config
|
||||
// Clone clones a Config.
|
||||
func (c *Config) Clone() *Config {
|
||||
copy := *c
|
||||
return ©
|
||||
|
||||
97
vendor/github.com/quic-go/quic-go/conn_id_generator.go
generated
vendored
97
vendor/github.com/quic-go/quic-go/conn_id_generator.go
generated
vendored
@@ -8,41 +8,67 @@ import (
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
type connRunnerCallbacks struct {
|
||||
AddConnectionID func(protocol.ConnectionID)
|
||||
RemoveConnectionID func(protocol.ConnectionID)
|
||||
RetireConnectionID func(protocol.ConnectionID)
|
||||
ReplaceWithClosed func([]protocol.ConnectionID, []byte)
|
||||
}
|
||||
|
||||
type connRunners map[transportID]connRunnerCallbacks
|
||||
|
||||
func (cr connRunners) AddConnectionID(id protocol.ConnectionID) {
|
||||
for _, c := range cr {
|
||||
c.AddConnectionID(id)
|
||||
}
|
||||
}
|
||||
|
||||
func (cr connRunners) RemoveConnectionID(id protocol.ConnectionID) {
|
||||
for _, c := range cr {
|
||||
c.RemoveConnectionID(id)
|
||||
}
|
||||
}
|
||||
|
||||
func (cr connRunners) RetireConnectionID(id protocol.ConnectionID) {
|
||||
for _, c := range cr {
|
||||
c.RetireConnectionID(id)
|
||||
}
|
||||
}
|
||||
|
||||
func (cr connRunners) ReplaceWithClosed(ids []protocol.ConnectionID, b []byte) {
|
||||
for _, c := range cr {
|
||||
c.ReplaceWithClosed(ids, b)
|
||||
}
|
||||
}
|
||||
|
||||
type connIDGenerator struct {
|
||||
generator ConnectionIDGenerator
|
||||
highestSeq uint64
|
||||
generator ConnectionIDGenerator
|
||||
highestSeq uint64
|
||||
connRunners connRunners
|
||||
|
||||
activeSrcConnIDs map[uint64]protocol.ConnectionID
|
||||
initialClientDestConnID *protocol.ConnectionID // nil for the client
|
||||
|
||||
addConnectionID func(protocol.ConnectionID)
|
||||
getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken
|
||||
removeConnectionID func(protocol.ConnectionID)
|
||||
retireConnectionID func(protocol.ConnectionID)
|
||||
replaceWithClosed func([]protocol.ConnectionID, []byte)
|
||||
queueControlFrame func(wire.Frame)
|
||||
statelessResetter *statelessResetter
|
||||
|
||||
queueControlFrame func(wire.Frame)
|
||||
}
|
||||
|
||||
func newConnIDGenerator(
|
||||
tID transportID,
|
||||
initialConnectionID protocol.ConnectionID,
|
||||
initialClientDestConnID *protocol.ConnectionID, // nil for the client
|
||||
addConnectionID func(protocol.ConnectionID),
|
||||
getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken,
|
||||
removeConnectionID func(protocol.ConnectionID),
|
||||
retireConnectionID func(protocol.ConnectionID),
|
||||
replaceWithClosed func([]protocol.ConnectionID, []byte),
|
||||
statelessResetter *statelessResetter,
|
||||
connRunner connRunnerCallbacks,
|
||||
queueControlFrame func(wire.Frame),
|
||||
generator ConnectionIDGenerator,
|
||||
) *connIDGenerator {
|
||||
m := &connIDGenerator{
|
||||
generator: generator,
|
||||
activeSrcConnIDs: make(map[uint64]protocol.ConnectionID),
|
||||
addConnectionID: addConnectionID,
|
||||
getStatelessResetToken: getStatelessResetToken,
|
||||
removeConnectionID: removeConnectionID,
|
||||
retireConnectionID: retireConnectionID,
|
||||
replaceWithClosed: replaceWithClosed,
|
||||
queueControlFrame: queueControlFrame,
|
||||
generator: generator,
|
||||
activeSrcConnIDs: make(map[uint64]protocol.ConnectionID),
|
||||
statelessResetter: statelessResetter,
|
||||
connRunners: map[transportID]connRunnerCallbacks{tID: connRunner},
|
||||
queueControlFrame: queueControlFrame,
|
||||
}
|
||||
m.activeSrcConnIDs[0] = initialConnectionID
|
||||
m.initialClientDestConnID = initialClientDestConnID
|
||||
@@ -85,7 +111,7 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect
|
||||
ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID),
|
||||
}
|
||||
}
|
||||
m.retireConnectionID(connID)
|
||||
m.connRunners.RetireConnectionID(connID)
|
||||
delete(m.activeSrcConnIDs, seq)
|
||||
// Don't issue a replacement for the initial connection ID.
|
||||
if seq == 0 {
|
||||
@@ -100,11 +126,11 @@ func (m *connIDGenerator) issueNewConnID() error {
|
||||
return err
|
||||
}
|
||||
m.activeSrcConnIDs[m.highestSeq+1] = connID
|
||||
m.addConnectionID(connID)
|
||||
m.connRunners.AddConnectionID(connID)
|
||||
m.queueControlFrame(&wire.NewConnectionIDFrame{
|
||||
SequenceNumber: m.highestSeq + 1,
|
||||
ConnectionID: connID,
|
||||
StatelessResetToken: m.getStatelessResetToken(connID),
|
||||
StatelessResetToken: m.statelessResetter.GetStatelessResetToken(connID),
|
||||
})
|
||||
m.highestSeq++
|
||||
return nil
|
||||
@@ -112,17 +138,17 @@ func (m *connIDGenerator) issueNewConnID() error {
|
||||
|
||||
func (m *connIDGenerator) SetHandshakeComplete() {
|
||||
if m.initialClientDestConnID != nil {
|
||||
m.retireConnectionID(*m.initialClientDestConnID)
|
||||
m.connRunners.RetireConnectionID(*m.initialClientDestConnID)
|
||||
m.initialClientDestConnID = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connIDGenerator) RemoveAll() {
|
||||
if m.initialClientDestConnID != nil {
|
||||
m.removeConnectionID(*m.initialClientDestConnID)
|
||||
m.connRunners.RemoveConnectionID(*m.initialClientDestConnID)
|
||||
}
|
||||
for _, connID := range m.activeSrcConnIDs {
|
||||
m.removeConnectionID(connID)
|
||||
m.connRunners.RemoveConnectionID(connID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,5 +160,20 @@ func (m *connIDGenerator) ReplaceWithClosed(connClose []byte) {
|
||||
for _, connID := range m.activeSrcConnIDs {
|
||||
connIDs = append(connIDs, connID)
|
||||
}
|
||||
m.replaceWithClosed(connIDs, connClose)
|
||||
m.connRunners.ReplaceWithClosed(connIDs, connClose)
|
||||
}
|
||||
|
||||
func (m *connIDGenerator) AddConnRunner(id transportID, r connRunnerCallbacks) {
|
||||
// The transport might have already been added earlier.
|
||||
// This happens if the application migrates back to and old path.
|
||||
if _, ok := m.connRunners[id]; ok {
|
||||
return
|
||||
}
|
||||
m.connRunners[id] = r
|
||||
if m.initialClientDestConnID != nil {
|
||||
r.AddConnectionID(*m.initialClientDestConnID)
|
||||
}
|
||||
for _, connID := range m.activeSrcConnIDs {
|
||||
r.AddConnectionID(connID)
|
||||
}
|
||||
}
|
||||
|
||||
167
vendor/github.com/quic-go/quic-go/conn_id_manager.go
generated
vendored
167
vendor/github.com/quic-go/quic-go/conn_id_manager.go
generated
vendored
@@ -2,11 +2,11 @@ package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/qerr"
|
||||
"github.com/quic-go/quic-go/internal/utils"
|
||||
list "github.com/quic-go/quic-go/internal/utils/linkedlist"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,10 @@ type newConnID struct {
|
||||
}
|
||||
|
||||
type connIDManager struct {
|
||||
queue list.List[newConnID]
|
||||
queue []newConnID
|
||||
|
||||
highestProbingID uint64
|
||||
pathProbing map[pathID]newConnID // initialized lazily
|
||||
|
||||
handshakeComplete bool
|
||||
activeSequenceNumber uint64
|
||||
@@ -35,6 +38,8 @@ type connIDManager struct {
|
||||
addStatelessResetToken func(protocol.StatelessResetToken)
|
||||
removeStatelessResetToken func(protocol.StatelessResetToken)
|
||||
queueControlFrame func(wire.Frame)
|
||||
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newConnIDManager(
|
||||
@@ -48,6 +53,7 @@ func newConnIDManager(
|
||||
addStatelessResetToken: addStatelessResetToken,
|
||||
removeStatelessResetToken: removeStatelessResetToken,
|
||||
queueControlFrame: queueControlFrame,
|
||||
queue: make([]newConnID, 0, protocol.MaxActiveConnectionIDs),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,36 +65,51 @@ func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
|
||||
if err := h.add(f); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
|
||||
if len(h.queue) >= protocol.MaxActiveConnectionIDs {
|
||||
return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error {
|
||||
if h.activeConnectionID.Len() == 0 {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received NEW_CONNECTION_ID frame but zero-length connection IDs are in use",
|
||||
}
|
||||
}
|
||||
// If the NEW_CONNECTION_ID frame is reordered, such that its sequence number is smaller than the currently active
|
||||
// connection ID or if it was already retired, send the RETIRE_CONNECTION_ID frame immediately.
|
||||
if f.SequenceNumber < h.activeSequenceNumber || f.SequenceNumber < h.highestRetired {
|
||||
if f.SequenceNumber < max(h.activeSequenceNumber, h.highestProbingID) || f.SequenceNumber < h.highestRetired {
|
||||
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||||
SequenceNumber: f.SequenceNumber,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.RetirePriorTo != 0 && h.pathProbing != nil {
|
||||
for id, entry := range h.pathProbing {
|
||||
if entry.SequenceNumber < f.RetirePriorTo {
|
||||
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||||
SequenceNumber: entry.SequenceNumber,
|
||||
})
|
||||
h.removeStatelessResetToken(entry.StatelessResetToken)
|
||||
delete(h.pathProbing, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Retire elements in the queue.
|
||||
// Doesn't retire the active connection ID.
|
||||
if f.RetirePriorTo > h.highestRetired {
|
||||
var next *list.Element[newConnID]
|
||||
for el := h.queue.Front(); el != nil; el = next {
|
||||
if el.Value.SequenceNumber >= f.RetirePriorTo {
|
||||
break
|
||||
var newQueue []newConnID
|
||||
for _, entry := range h.queue {
|
||||
if entry.SequenceNumber >= f.RetirePriorTo {
|
||||
newQueue = append(newQueue, entry)
|
||||
} else {
|
||||
h.queueControlFrame(&wire.RetireConnectionIDFrame{SequenceNumber: entry.SequenceNumber})
|
||||
}
|
||||
next = el.Next()
|
||||
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||||
SequenceNumber: el.Value.SequenceNumber,
|
||||
})
|
||||
h.queue.Remove(el)
|
||||
}
|
||||
h.queue = newQueue
|
||||
h.highestRetired = f.RetirePriorTo
|
||||
}
|
||||
|
||||
@@ -109,39 +130,43 @@ func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error {
|
||||
}
|
||||
|
||||
func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
|
||||
// insert a new element at the end
|
||||
if h.queue.Len() == 0 || h.queue.Back().Value.SequenceNumber < seq {
|
||||
h.queue.PushBack(newConnID{
|
||||
// fast path: add to the end of the queue
|
||||
if len(h.queue) == 0 || h.queue[len(h.queue)-1].SequenceNumber < seq {
|
||||
h.queue = append(h.queue, newConnID{
|
||||
SequenceNumber: seq,
|
||||
ConnectionID: connID,
|
||||
StatelessResetToken: resetToken,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// insert a new element somewhere in the middle
|
||||
for el := h.queue.Front(); el != nil; el = el.Next() {
|
||||
if el.Value.SequenceNumber == seq {
|
||||
if el.Value.ConnectionID != connID {
|
||||
|
||||
// slow path: insert in the middle
|
||||
for i, entry := range h.queue {
|
||||
if entry.SequenceNumber == seq {
|
||||
if entry.ConnectionID != connID {
|
||||
return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq)
|
||||
}
|
||||
if el.Value.StatelessResetToken != resetToken {
|
||||
if entry.StatelessResetToken != resetToken {
|
||||
return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq)
|
||||
}
|
||||
break
|
||||
return nil
|
||||
}
|
||||
if el.Value.SequenceNumber > seq {
|
||||
h.queue.InsertBefore(newConnID{
|
||||
|
||||
// insert at the correct position to maintain sorted order
|
||||
if entry.SequenceNumber > seq {
|
||||
h.queue = slices.Insert(h.queue, i, newConnID{
|
||||
SequenceNumber: seq,
|
||||
ConnectionID: connID,
|
||||
StatelessResetToken: resetToken,
|
||||
}, el)
|
||||
break
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil // unreachable
|
||||
}
|
||||
|
||||
func (h *connIDManager) updateConnectionID() {
|
||||
h.assertNotClosed()
|
||||
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||||
SequenceNumber: h.activeSequenceNumber,
|
||||
})
|
||||
@@ -150,7 +175,8 @@ func (h *connIDManager) updateConnectionID() {
|
||||
h.removeStatelessResetToken(*h.activeStatelessResetToken)
|
||||
}
|
||||
|
||||
front := h.queue.Remove(h.queue.Front())
|
||||
front := h.queue[0]
|
||||
h.queue = h.queue[1:]
|
||||
h.activeSequenceNumber = front.SequenceNumber
|
||||
h.activeConnectionID = front.ConnectionID
|
||||
h.activeStatelessResetToken = &front.StatelessResetToken
|
||||
@@ -160,9 +186,15 @@ func (h *connIDManager) updateConnectionID() {
|
||||
}
|
||||
|
||||
func (h *connIDManager) Close() {
|
||||
h.closed = true
|
||||
if h.activeStatelessResetToken != nil {
|
||||
h.removeStatelessResetToken(*h.activeStatelessResetToken)
|
||||
}
|
||||
if h.pathProbing != nil {
|
||||
for _, entry := range h.pathProbing {
|
||||
h.removeStatelessResetToken(entry.StatelessResetToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is called when the server performs a Retry
|
||||
@@ -176,6 +208,7 @@ func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) {
|
||||
|
||||
// is called when the server provides a stateless reset token in the transport parameters
|
||||
func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) {
|
||||
h.assertNotClosed()
|
||||
if h.activeSequenceNumber != 0 {
|
||||
panic("expected first connection ID to have sequence number 0")
|
||||
}
|
||||
@@ -192,17 +225,18 @@ func (h *connIDManager) shouldUpdateConnID() bool {
|
||||
return false
|
||||
}
|
||||
// initiate the first change as early as possible (after handshake completion)
|
||||
if h.queue.Len() > 0 && h.activeSequenceNumber == 0 {
|
||||
if len(h.queue) > 0 && h.activeSequenceNumber == 0 {
|
||||
return true
|
||||
}
|
||||
// For later changes, only change if
|
||||
// 1. The queue of connection IDs is filled more than 50%.
|
||||
// 2. We sent at least PacketsPerConnectionID packets
|
||||
return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs &&
|
||||
return 2*len(h.queue) >= protocol.MaxActiveConnectionIDs &&
|
||||
h.packetsSinceLastChange >= h.packetsPerConnectionID
|
||||
}
|
||||
|
||||
func (h *connIDManager) Get() protocol.ConnectionID {
|
||||
h.assertNotClosed()
|
||||
if h.shouldUpdateConnID() {
|
||||
h.updateConnectionID()
|
||||
}
|
||||
@@ -212,3 +246,76 @@ func (h *connIDManager) Get() protocol.ConnectionID {
|
||||
func (h *connIDManager) SetHandshakeComplete() {
|
||||
h.handshakeComplete = true
|
||||
}
|
||||
|
||||
// GetConnIDForPath retrieves a connection ID for a new path (i.e. not the active one).
|
||||
// Once a connection ID is allocated for a path, it cannot be used for a different path.
|
||||
// When called with the same pathID, it will return the same connection ID,
|
||||
// unless the peer requested that this connection ID be retired.
|
||||
func (h *connIDManager) GetConnIDForPath(id pathID) (protocol.ConnectionID, bool) {
|
||||
h.assertNotClosed()
|
||||
// if we're using zero-length connection IDs, we don't need to change the connection ID
|
||||
if h.activeConnectionID.Len() == 0 {
|
||||
return protocol.ConnectionID{}, true
|
||||
}
|
||||
|
||||
if h.pathProbing == nil {
|
||||
h.pathProbing = make(map[pathID]newConnID)
|
||||
}
|
||||
entry, ok := h.pathProbing[id]
|
||||
if ok {
|
||||
return entry.ConnectionID, true
|
||||
}
|
||||
if len(h.queue) == 0 {
|
||||
return protocol.ConnectionID{}, false
|
||||
}
|
||||
front := h.queue[0]
|
||||
h.queue = h.queue[1:]
|
||||
h.pathProbing[id] = front
|
||||
h.highestProbingID = front.SequenceNumber
|
||||
h.addStatelessResetToken(front.StatelessResetToken)
|
||||
return front.ConnectionID, true
|
||||
}
|
||||
|
||||
func (h *connIDManager) RetireConnIDForPath(pathID pathID) {
|
||||
h.assertNotClosed()
|
||||
// if we're using zero-length connection IDs, we don't need to change the connection ID
|
||||
if h.activeConnectionID.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
entry, ok := h.pathProbing[pathID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||||
SequenceNumber: entry.SequenceNumber,
|
||||
})
|
||||
h.removeStatelessResetToken(entry.StatelessResetToken)
|
||||
delete(h.pathProbing, pathID)
|
||||
}
|
||||
|
||||
func (h *connIDManager) IsActiveStatelessResetToken(token protocol.StatelessResetToken) bool {
|
||||
if h.activeStatelessResetToken != nil {
|
||||
if *h.activeStatelessResetToken == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if h.pathProbing != nil {
|
||||
for _, entry := range h.pathProbing {
|
||||
if entry.StatelessResetToken == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Using the connIDManager after it has been closed can have disastrous effects:
|
||||
// If the connection ID is rotated, a new entry would be inserted into the packet handler map,
|
||||
// leading to a memory leak of the connection struct.
|
||||
// See https://github.com/quic-go/quic-go/pull/4852 for more details.
|
||||
func (h *connIDManager) assertNotClosed() {
|
||||
if h.closed {
|
||||
panic("connection ID manager is closed")
|
||||
}
|
||||
}
|
||||
|
||||
1152
vendor/github.com/quic-go/quic-go/connection.go
generated
vendored
1152
vendor/github.com/quic-go/quic-go/connection.go
generated
vendored
File diff suppressed because it is too large
Load Diff
168
vendor/github.com/quic-go/quic-go/connection_logging.go
generated
vendored
Normal file
168
vendor/github.com/quic-go/quic-go/connection_logging.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/ackhandler"
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
)
|
||||
|
||||
// ConvertFrame converts a wire.Frame into a logging.Frame.
|
||||
// This makes it possible for external packages to access the frames.
|
||||
// Furthermore, it removes the data slices from CRYPTO and STREAM frames.
|
||||
func toLoggingFrame(frame wire.Frame) logging.Frame {
|
||||
switch f := frame.(type) {
|
||||
case *wire.AckFrame:
|
||||
// We use a pool for ACK frames.
|
||||
// Implementations of the tracer interface may hold on to frames, so we need to make a copy here.
|
||||
return toLoggingAckFrame(f)
|
||||
case *wire.CryptoFrame:
|
||||
return &logging.CryptoFrame{
|
||||
Offset: f.Offset,
|
||||
Length: protocol.ByteCount(len(f.Data)),
|
||||
}
|
||||
case *wire.StreamFrame:
|
||||
return &logging.StreamFrame{
|
||||
StreamID: f.StreamID,
|
||||
Offset: f.Offset,
|
||||
Length: f.DataLen(),
|
||||
Fin: f.Fin,
|
||||
}
|
||||
case *wire.DatagramFrame:
|
||||
return &logging.DatagramFrame{
|
||||
Length: logging.ByteCount(len(f.Data)),
|
||||
}
|
||||
default:
|
||||
return logging.Frame(frame)
|
||||
}
|
||||
}
|
||||
|
||||
func toLoggingAckFrame(f *wire.AckFrame) *logging.AckFrame {
|
||||
ack := &logging.AckFrame{
|
||||
AckRanges: slices.Clone(f.AckRanges),
|
||||
DelayTime: f.DelayTime,
|
||||
ECNCE: f.ECNCE,
|
||||
ECT0: f.ECT0,
|
||||
ECT1: f.ECT1,
|
||||
}
|
||||
return ack
|
||||
}
|
||||
|
||||
func (s *connection) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN) {
|
||||
// quic-go logging
|
||||
if s.logger.Debug() {
|
||||
p.header.Log(s.logger)
|
||||
if p.ack != nil {
|
||||
wire.LogFrame(s.logger, p.ack, true)
|
||||
}
|
||||
for _, frame := range p.frames {
|
||||
wire.LogFrame(s.logger, frame.Frame, true)
|
||||
}
|
||||
for _, frame := range p.streamFrames {
|
||||
wire.LogFrame(s.logger, frame.Frame, true)
|
||||
}
|
||||
}
|
||||
|
||||
// tracing
|
||||
if s.tracer != nil && s.tracer.SentLongHeaderPacket != nil {
|
||||
frames := make([]logging.Frame, 0, len(p.frames))
|
||||
for _, f := range p.frames {
|
||||
frames = append(frames, toLoggingFrame(f.Frame))
|
||||
}
|
||||
for _, f := range p.streamFrames {
|
||||
frames = append(frames, toLoggingFrame(f.Frame))
|
||||
}
|
||||
var ack *logging.AckFrame
|
||||
if p.ack != nil {
|
||||
ack = toLoggingAckFrame(p.ack)
|
||||
}
|
||||
s.tracer.SentLongHeaderPacket(p.header, p.length, ecn, ack, frames)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *connection) logShortHeaderPacket(
|
||||
destConnID protocol.ConnectionID,
|
||||
ackFrame *wire.AckFrame,
|
||||
frames []ackhandler.Frame,
|
||||
streamFrames []ackhandler.StreamFrame,
|
||||
pn protocol.PacketNumber,
|
||||
pnLen protocol.PacketNumberLen,
|
||||
kp protocol.KeyPhaseBit,
|
||||
ecn protocol.ECN,
|
||||
size protocol.ByteCount,
|
||||
isCoalesced bool,
|
||||
) {
|
||||
if s.logger.Debug() && !isCoalesced {
|
||||
s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", pn, size, s.logID, ecn)
|
||||
}
|
||||
// quic-go logging
|
||||
if s.logger.Debug() {
|
||||
wire.LogShortHeader(s.logger, destConnID, pn, pnLen, kp)
|
||||
if ackFrame != nil {
|
||||
wire.LogFrame(s.logger, ackFrame, true)
|
||||
}
|
||||
for _, f := range frames {
|
||||
wire.LogFrame(s.logger, f.Frame, true)
|
||||
}
|
||||
for _, f := range streamFrames {
|
||||
wire.LogFrame(s.logger, f.Frame, true)
|
||||
}
|
||||
}
|
||||
|
||||
// tracing
|
||||
if s.tracer != nil && s.tracer.SentShortHeaderPacket != nil {
|
||||
fs := make([]logging.Frame, 0, len(frames)+len(streamFrames))
|
||||
for _, f := range frames {
|
||||
fs = append(fs, toLoggingFrame(f.Frame))
|
||||
}
|
||||
for _, f := range streamFrames {
|
||||
fs = append(fs, toLoggingFrame(f.Frame))
|
||||
}
|
||||
var ack *logging.AckFrame
|
||||
if ackFrame != nil {
|
||||
ack = toLoggingAckFrame(ackFrame)
|
||||
}
|
||||
s.tracer.SentShortHeaderPacket(
|
||||
&logging.ShortHeader{DestConnectionID: destConnID, PacketNumber: pn, PacketNumberLen: pnLen, KeyPhase: kp},
|
||||
size,
|
||||
ecn,
|
||||
ack,
|
||||
fs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *connection) logCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN) {
|
||||
if s.logger.Debug() {
|
||||
// There's a short period between dropping both Initial and Handshake keys and completion of the handshake,
|
||||
// during which we might call PackCoalescedPacket but just pack a short header packet.
|
||||
if len(packet.longHdrPackets) == 0 && packet.shortHdrPacket != nil {
|
||||
s.logShortHeaderPacket(
|
||||
packet.shortHdrPacket.DestConnID,
|
||||
packet.shortHdrPacket.Ack,
|
||||
packet.shortHdrPacket.Frames,
|
||||
packet.shortHdrPacket.StreamFrames,
|
||||
packet.shortHdrPacket.PacketNumber,
|
||||
packet.shortHdrPacket.PacketNumberLen,
|
||||
packet.shortHdrPacket.KeyPhase,
|
||||
ecn,
|
||||
packet.shortHdrPacket.Length,
|
||||
false,
|
||||
)
|
||||
return
|
||||
}
|
||||
if len(packet.longHdrPackets) > 1 {
|
||||
s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.longHdrPackets), packet.buffer.Len(), s.logID)
|
||||
} else {
|
||||
s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.longHdrPackets[0].header.PacketNumber, packet.buffer.Len(), s.logID, packet.longHdrPackets[0].EncryptionLevel())
|
||||
}
|
||||
}
|
||||
for _, p := range packet.longHdrPackets {
|
||||
s.logLongHeaderPacket(p, ecn)
|
||||
}
|
||||
if p := packet.shortHdrPacket; p != nil {
|
||||
s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, p.Length, true)
|
||||
}
|
||||
}
|
||||
49
vendor/github.com/quic-go/quic-go/crypto_stream.go
generated
vendored
49
vendor/github.com/quic-go/quic-go/crypto_stream.go
generated
vendored
@@ -2,27 +2,14 @@ package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/qerr"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
type cryptoStream interface {
|
||||
// for receiving data
|
||||
HandleCryptoFrame(*wire.CryptoFrame) error
|
||||
GetCryptoData() []byte
|
||||
Finish() error
|
||||
// for sending data
|
||||
io.Writer
|
||||
HasData() bool
|
||||
PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame
|
||||
}
|
||||
|
||||
type cryptoStreamImpl struct {
|
||||
queue *frameSorter
|
||||
msgBuf []byte
|
||||
type cryptoStream struct {
|
||||
queue frameSorter
|
||||
|
||||
highestOffset protocol.ByteCount
|
||||
finished bool
|
||||
@@ -31,11 +18,11 @@ type cryptoStreamImpl struct {
|
||||
writeBuf []byte
|
||||
}
|
||||
|
||||
func newCryptoStream() cryptoStream {
|
||||
return &cryptoStreamImpl{queue: newFrameSorter()}
|
||||
func newCryptoStream() *cryptoStream {
|
||||
return &cryptoStream{queue: *newFrameSorter()}
|
||||
}
|
||||
|
||||
func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
|
||||
func (s *cryptoStream) HandleCryptoFrame(f *wire.CryptoFrame) error {
|
||||
highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
|
||||
if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
|
||||
return &qerr.TransportError{
|
||||
@@ -56,26 +43,16 @@ func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
|
||||
return nil
|
||||
}
|
||||
s.highestOffset = max(s.highestOffset, highestOffset)
|
||||
if err := s.queue.Push(f.Data, f.Offset, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
_, data, _ := s.queue.Pop()
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
s.msgBuf = append(s.msgBuf, data...)
|
||||
}
|
||||
return s.queue.Push(f.Data, f.Offset, nil)
|
||||
}
|
||||
|
||||
// GetCryptoData retrieves data that was received in CRYPTO frames
|
||||
func (s *cryptoStreamImpl) GetCryptoData() []byte {
|
||||
b := s.msgBuf
|
||||
s.msgBuf = nil
|
||||
return b
|
||||
func (s *cryptoStream) GetCryptoData() []byte {
|
||||
_, data, _ := s.queue.Pop()
|
||||
return data
|
||||
}
|
||||
|
||||
func (s *cryptoStreamImpl) Finish() error {
|
||||
func (s *cryptoStream) Finish() error {
|
||||
if s.queue.HasMoreData() {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
@@ -87,16 +64,16 @@ func (s *cryptoStreamImpl) Finish() error {
|
||||
}
|
||||
|
||||
// Writes writes data that should be sent out in CRYPTO frames
|
||||
func (s *cryptoStreamImpl) Write(p []byte) (int, error) {
|
||||
func (s *cryptoStream) Write(p []byte) (int, error) {
|
||||
s.writeBuf = append(s.writeBuf, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (s *cryptoStreamImpl) HasData() bool {
|
||||
func (s *cryptoStream) HasData() bool {
|
||||
return len(s.writeBuf) > 0
|
||||
}
|
||||
|
||||
func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame {
|
||||
func (s *cryptoStream) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame {
|
||||
f := &wire.CryptoFrame{Offset: s.writeOffset}
|
||||
n := min(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf)))
|
||||
f.Data = s.writeBuf[:n]
|
||||
|
||||
51
vendor/github.com/quic-go/quic-go/crypto_stream_manager.go
generated
vendored
51
vendor/github.com/quic-go/quic-go/crypto_stream_manager.go
generated
vendored
@@ -3,32 +3,22 @@ package quic
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/handshake"
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
type cryptoDataHandler interface {
|
||||
HandleMessage([]byte, protocol.EncryptionLevel) error
|
||||
NextEvent() handshake.Event
|
||||
}
|
||||
|
||||
type cryptoStreamManager struct {
|
||||
cryptoHandler cryptoDataHandler
|
||||
|
||||
initialStream cryptoStream
|
||||
handshakeStream cryptoStream
|
||||
oneRTTStream cryptoStream
|
||||
initialStream *cryptoStream
|
||||
handshakeStream *cryptoStream
|
||||
oneRTTStream *cryptoStream
|
||||
}
|
||||
|
||||
func newCryptoStreamManager(
|
||||
cryptoHandler cryptoDataHandler,
|
||||
initialStream cryptoStream,
|
||||
handshakeStream cryptoStream,
|
||||
oneRTTStream cryptoStream,
|
||||
initialStream *cryptoStream,
|
||||
handshakeStream *cryptoStream,
|
||||
oneRTTStream *cryptoStream,
|
||||
) *cryptoStreamManager {
|
||||
return &cryptoStreamManager{
|
||||
cryptoHandler: cryptoHandler,
|
||||
initialStream: initialStream,
|
||||
handshakeStream: handshakeStream,
|
||||
oneRTTStream: oneRTTStream,
|
||||
@@ -36,7 +26,7 @@ func newCryptoStreamManager(
|
||||
}
|
||||
|
||||
func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
|
||||
var str cryptoStream
|
||||
var str *cryptoStream
|
||||
//nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets.
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
@@ -48,18 +38,23 @@ func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLeve
|
||||
default:
|
||||
return fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel)
|
||||
}
|
||||
if err := str.HandleCryptoFrame(frame); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
data := str.GetCryptoData()
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
if err := m.cryptoHandler.HandleMessage(data, encLevel); err != nil {
|
||||
return err
|
||||
}
|
||||
return str.HandleCryptoFrame(frame)
|
||||
}
|
||||
|
||||
func (m *cryptoStreamManager) GetCryptoData(encLevel protocol.EncryptionLevel) []byte {
|
||||
var str *cryptoStream
|
||||
//nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets.
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
str = m.initialStream
|
||||
case protocol.EncryptionHandshake:
|
||||
str = m.handshakeStream
|
||||
case protocol.Encryption1RTT:
|
||||
str = m.oneRTTStream
|
||||
default:
|
||||
panic(fmt.Sprintf("received CRYPTO frame with unexpected encryption level: %s", encLevel))
|
||||
}
|
||||
return str.GetCryptoData()
|
||||
}
|
||||
|
||||
func (m *cryptoStreamManager) GetPostHandshakeData(maxSize protocol.ByteCount) *wire.CryptoFrame {
|
||||
|
||||
8
vendor/github.com/quic-go/quic-go/errors.go
generated
vendored
8
vendor/github.com/quic-go/quic-go/errors.go
generated
vendored
@@ -50,8 +50,8 @@ type StreamError struct {
|
||||
}
|
||||
|
||||
func (e *StreamError) Is(target error) bool {
|
||||
_, ok := target.(*StreamError)
|
||||
return ok
|
||||
t, ok := target.(*StreamError)
|
||||
return ok && e.StreamID == t.StreamID && e.ErrorCode == t.ErrorCode && e.Remote == t.Remote
|
||||
}
|
||||
|
||||
func (e *StreamError) Error() string {
|
||||
@@ -68,8 +68,8 @@ type DatagramTooLargeError struct {
|
||||
}
|
||||
|
||||
func (e *DatagramTooLargeError) Is(target error) bool {
|
||||
_, ok := target.(*DatagramTooLargeError)
|
||||
return ok
|
||||
t, ok := target.(*DatagramTooLargeError)
|
||||
return ok && e.MaxDatagramPayloadSize == t.MaxDatagramPayloadSize
|
||||
}
|
||||
|
||||
func (e *DatagramTooLargeError) Error() string { return "DATAGRAM frame too large" }
|
||||
|
||||
265
vendor/github.com/quic-go/quic-go/framer.go
generated
vendored
265
vendor/github.com/quic-go/quic-go/framer.go
generated
vendored
@@ -1,64 +1,54 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/ackhandler"
|
||||
"github.com/quic-go/quic-go/internal/flowcontrol"
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/utils/ringbuffer"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
"github.com/quic-go/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
type framer interface {
|
||||
HasData() bool
|
||||
|
||||
QueueControlFrame(wire.Frame)
|
||||
AppendControlFrames([]ackhandler.Frame, protocol.ByteCount, protocol.Version) ([]ackhandler.Frame, protocol.ByteCount)
|
||||
|
||||
AddActiveStream(protocol.StreamID)
|
||||
AppendStreamFrames([]ackhandler.StreamFrame, protocol.ByteCount, protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount)
|
||||
|
||||
Handle0RTTRejection() error
|
||||
|
||||
// QueuedTooManyControlFrames says if the control frame queue exceeded its maximum queue length.
|
||||
// This is a hack.
|
||||
// It is easier to implement than propagating an error return value in QueueControlFrame.
|
||||
// The correct solution would be to queue frames with their respective structs.
|
||||
// See https://github.com/quic-go/quic-go/issues/4271 for the queueing of stream-related control frames.
|
||||
QueuedTooManyControlFrames() bool
|
||||
}
|
||||
|
||||
const (
|
||||
maxPathResponses = 256
|
||||
maxControlFrames = 16 << 10
|
||||
)
|
||||
|
||||
type framerI struct {
|
||||
// This is the largest possible size of a stream-related control frame
|
||||
// (which is the RESET_STREAM frame).
|
||||
const maxStreamControlFrameSize = 25
|
||||
|
||||
type streamControlFrameGetter interface {
|
||||
getControlFrame(time.Time) (_ ackhandler.Frame, ok, hasMore bool)
|
||||
}
|
||||
|
||||
type framer struct {
|
||||
mutex sync.Mutex
|
||||
|
||||
streamGetter streamGetter
|
||||
|
||||
activeStreams map[protocol.StreamID]struct{}
|
||||
streamQueue ringbuffer.RingBuffer[protocol.StreamID]
|
||||
activeStreams map[protocol.StreamID]sendStreamI
|
||||
streamQueue ringbuffer.RingBuffer[protocol.StreamID]
|
||||
streamsWithControlFrames map[protocol.StreamID]streamControlFrameGetter
|
||||
|
||||
controlFrameMutex sync.Mutex
|
||||
controlFrames []wire.Frame
|
||||
pathResponses []*wire.PathResponseFrame
|
||||
connFlowController flowcontrol.ConnectionFlowController
|
||||
queuedTooManyControlFrames bool
|
||||
}
|
||||
|
||||
var _ framer = &framerI{}
|
||||
|
||||
func newFramer(streamGetter streamGetter) framer {
|
||||
return &framerI{
|
||||
streamGetter: streamGetter,
|
||||
activeStreams: make(map[protocol.StreamID]struct{}),
|
||||
func newFramer(connFlowController flowcontrol.ConnectionFlowController) *framer {
|
||||
return &framer{
|
||||
activeStreams: make(map[protocol.StreamID]sendStreamI),
|
||||
streamsWithControlFrames: make(map[protocol.StreamID]streamControlFrameGetter),
|
||||
connFlowController: connFlowController,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *framerI) HasData() bool {
|
||||
func (f *framer) HasData() bool {
|
||||
f.mutex.Lock()
|
||||
hasData := !f.streamQueue.Empty()
|
||||
f.mutex.Unlock()
|
||||
@@ -67,10 +57,10 @@ func (f *framerI) HasData() bool {
|
||||
}
|
||||
f.controlFrameMutex.Lock()
|
||||
defer f.controlFrameMutex.Unlock()
|
||||
return len(f.controlFrames) > 0 || len(f.pathResponses) > 0
|
||||
return len(f.streamsWithControlFrames) > 0 || len(f.controlFrames) > 0 || len(f.pathResponses) > 0
|
||||
}
|
||||
|
||||
func (f *framerI) QueueControlFrame(frame wire.Frame) {
|
||||
func (f *framer) QueueControlFrame(frame wire.Frame) {
|
||||
f.controlFrameMutex.Lock()
|
||||
defer f.controlFrameMutex.Unlock()
|
||||
|
||||
@@ -92,10 +82,80 @@ func (f *framerI) QueueControlFrame(frame wire.Frame) {
|
||||
f.controlFrames = append(f.controlFrames, frame)
|
||||
}
|
||||
|
||||
func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) {
|
||||
func (f *framer) Append(
|
||||
frames []ackhandler.Frame,
|
||||
streamFrames []ackhandler.StreamFrame,
|
||||
maxLen protocol.ByteCount,
|
||||
now time.Time,
|
||||
v protocol.Version,
|
||||
) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) {
|
||||
f.controlFrameMutex.Lock()
|
||||
defer f.controlFrameMutex.Unlock()
|
||||
frames, controlFrameLen := f.appendControlFrames(frames, maxLen, now, v)
|
||||
maxLen -= controlFrameLen
|
||||
|
||||
var lastFrame ackhandler.StreamFrame
|
||||
var streamFrameLen protocol.ByteCount
|
||||
f.mutex.Lock()
|
||||
// pop STREAM frames, until less than 128 bytes are left in the packet
|
||||
numActiveStreams := f.streamQueue.Len()
|
||||
for i := 0; i < numActiveStreams; i++ {
|
||||
if protocol.MinStreamFrameSize > maxLen {
|
||||
break
|
||||
}
|
||||
sf, blocked := f.getNextStreamFrame(maxLen, v)
|
||||
if sf.Frame != nil {
|
||||
streamFrames = append(streamFrames, sf)
|
||||
maxLen -= sf.Frame.Length(v)
|
||||
lastFrame = sf
|
||||
streamFrameLen += sf.Frame.Length(v)
|
||||
}
|
||||
// If the stream just became blocked on stream flow control, attempt to pack the
|
||||
// STREAM_DATA_BLOCKED into the same packet.
|
||||
if blocked != nil {
|
||||
l := blocked.Length(v)
|
||||
// In case it doesn't fit, queue it for the next packet.
|
||||
if maxLen < l {
|
||||
f.controlFrames = append(f.controlFrames, blocked)
|
||||
break
|
||||
}
|
||||
frames = append(frames, ackhandler.Frame{Frame: blocked})
|
||||
maxLen -= l
|
||||
controlFrameLen += l
|
||||
}
|
||||
}
|
||||
|
||||
// The only way to become blocked on connection-level flow control is by sending STREAM frames.
|
||||
if isBlocked, offset := f.connFlowController.IsNewlyBlocked(); isBlocked {
|
||||
blocked := &wire.DataBlockedFrame{MaximumData: offset}
|
||||
l := blocked.Length(v)
|
||||
// In case it doesn't fit, queue it for the next packet.
|
||||
if maxLen >= l {
|
||||
frames = append(frames, ackhandler.Frame{Frame: blocked})
|
||||
controlFrameLen += l
|
||||
} else {
|
||||
f.controlFrames = append(f.controlFrames, blocked)
|
||||
}
|
||||
}
|
||||
|
||||
f.mutex.Unlock()
|
||||
f.controlFrameMutex.Unlock()
|
||||
|
||||
if lastFrame.Frame != nil {
|
||||
// account for the smaller size of the last STREAM frame
|
||||
streamFrameLen -= lastFrame.Frame.Length(v)
|
||||
lastFrame.Frame.DataLenPresent = false
|
||||
streamFrameLen += lastFrame.Frame.Length(v)
|
||||
}
|
||||
|
||||
return frames, streamFrames, controlFrameLen + streamFrameLen
|
||||
}
|
||||
|
||||
func (f *framer) appendControlFrames(
|
||||
frames []ackhandler.Frame,
|
||||
maxLen protocol.ByteCount,
|
||||
now time.Time,
|
||||
v protocol.Version,
|
||||
) ([]ackhandler.Frame, protocol.ByteCount) {
|
||||
var length protocol.ByteCount
|
||||
// add a PATH_RESPONSE first, but only pack a single PATH_RESPONSE per packet
|
||||
if len(f.pathResponses) > 0 {
|
||||
@@ -108,6 +168,29 @@ func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol
|
||||
}
|
||||
}
|
||||
|
||||
// add stream-related control frames
|
||||
for id, str := range f.streamsWithControlFrames {
|
||||
start:
|
||||
remainingLen := maxLen - length
|
||||
if remainingLen <= maxStreamControlFrameSize {
|
||||
break
|
||||
}
|
||||
fr, ok, hasMore := str.getControlFrame(now)
|
||||
if !hasMore {
|
||||
delete(f.streamsWithControlFrames, id)
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
frames = append(frames, fr)
|
||||
length += fr.Frame.Length(v)
|
||||
if hasMore {
|
||||
// It is rare that a stream has more than one control frame to queue.
|
||||
// We don't want to spawn another loop for just to cover that case.
|
||||
goto start
|
||||
}
|
||||
}
|
||||
|
||||
for len(f.controlFrames) > 0 {
|
||||
frame := f.controlFrames[len(f.controlFrames)-1]
|
||||
frameLen := frame.Length(v)
|
||||
@@ -118,76 +201,77 @@ func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol
|
||||
length += frameLen
|
||||
f.controlFrames = f.controlFrames[:len(f.controlFrames)-1]
|
||||
}
|
||||
|
||||
return frames, length
|
||||
}
|
||||
|
||||
func (f *framerI) QueuedTooManyControlFrames() bool {
|
||||
// QueuedTooManyControlFrames says if the control frame queue exceeded its maximum queue length.
|
||||
// This is a hack.
|
||||
// It is easier to implement than propagating an error return value in QueueControlFrame.
|
||||
// The correct solution would be to queue frames with their respective structs.
|
||||
// See https://github.com/quic-go/quic-go/issues/4271 for the queueing of stream-related control frames.
|
||||
func (f *framer) QueuedTooManyControlFrames() bool {
|
||||
return f.queuedTooManyControlFrames
|
||||
}
|
||||
|
||||
func (f *framerI) AddActiveStream(id protocol.StreamID) {
|
||||
func (f *framer) AddActiveStream(id protocol.StreamID, str sendStreamI) {
|
||||
f.mutex.Lock()
|
||||
if _, ok := f.activeStreams[id]; !ok {
|
||||
f.streamQueue.PushBack(id)
|
||||
f.activeStreams[id] = struct{}{}
|
||||
f.activeStreams[id] = str
|
||||
}
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (f *framerI) AppendStreamFrames(frames []ackhandler.StreamFrame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) {
|
||||
startLen := len(frames)
|
||||
var length protocol.ByteCount
|
||||
func (f *framer) AddStreamWithControlFrames(id protocol.StreamID, str streamControlFrameGetter) {
|
||||
f.controlFrameMutex.Lock()
|
||||
if _, ok := f.streamsWithControlFrames[id]; !ok {
|
||||
f.streamsWithControlFrames[id] = str
|
||||
}
|
||||
f.controlFrameMutex.Unlock()
|
||||
}
|
||||
|
||||
// RemoveActiveStream is called when a stream completes.
|
||||
func (f *framer) RemoveActiveStream(id protocol.StreamID) {
|
||||
f.mutex.Lock()
|
||||
// pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet
|
||||
numActiveStreams := f.streamQueue.Len()
|
||||
for i := 0; i < numActiveStreams; i++ {
|
||||
if protocol.MinStreamFrameSize+length > maxLen {
|
||||
break
|
||||
}
|
||||
id := f.streamQueue.PopFront()
|
||||
// This should never return an error. Better check it anyway.
|
||||
// The stream will only be in the streamQueue, if it enqueued itself there.
|
||||
str, err := f.streamGetter.GetOrOpenSendStream(id)
|
||||
// The stream can be nil if it completed after it said it had data.
|
||||
if str == nil || err != nil {
|
||||
delete(f.activeStreams, id)
|
||||
continue
|
||||
}
|
||||
remainingLen := maxLen - length
|
||||
// For the last STREAM frame, we'll remove the DataLen field later.
|
||||
// Therefore, we can pretend to have more bytes available when popping
|
||||
// the STREAM frame (which will always have the DataLen set).
|
||||
remainingLen += protocol.ByteCount(quicvarint.Len(uint64(remainingLen)))
|
||||
frame, ok, hasMoreData := str.popStreamFrame(remainingLen, v)
|
||||
if hasMoreData { // put the stream back in the queue (at the end)
|
||||
f.streamQueue.PushBack(id)
|
||||
} else { // no more data to send. Stream is not active
|
||||
delete(f.activeStreams, id)
|
||||
}
|
||||
// The frame can be "nil"
|
||||
// * if the receiveStream was canceled after it said it had data
|
||||
// * the remaining size doesn't allow us to add another STREAM frame
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
frames = append(frames, frame)
|
||||
length += frame.Frame.Length(v)
|
||||
}
|
||||
delete(f.activeStreams, id)
|
||||
// We don't delete the stream from the streamQueue,
|
||||
// since we'd have to iterate over the ringbuffer.
|
||||
// Instead, we check if the stream is still in activeStreams when appending STREAM frames.
|
||||
f.mutex.Unlock()
|
||||
if len(frames) > startLen {
|
||||
l := frames[len(frames)-1].Frame.Length(v)
|
||||
// account for the smaller size of the last STREAM frame
|
||||
frames[len(frames)-1].Frame.DataLenPresent = false
|
||||
length += frames[len(frames)-1].Frame.Length(v) - l
|
||||
}
|
||||
return frames, length
|
||||
}
|
||||
|
||||
func (f *framerI) Handle0RTTRejection() error {
|
||||
func (f *framer) getNextStreamFrame(maxLen protocol.ByteCount, v protocol.Version) (ackhandler.StreamFrame, *wire.StreamDataBlockedFrame) {
|
||||
id := f.streamQueue.PopFront()
|
||||
// This should never return an error. Better check it anyway.
|
||||
// The stream will only be in the streamQueue, if it enqueued itself there.
|
||||
str, ok := f.activeStreams[id]
|
||||
// The stream might have been removed after being enqueued.
|
||||
if !ok {
|
||||
return ackhandler.StreamFrame{}, nil
|
||||
}
|
||||
// For the last STREAM frame, we'll remove the DataLen field later.
|
||||
// Therefore, we can pretend to have more bytes available when popping
|
||||
// the STREAM frame (which will always have the DataLen set).
|
||||
maxLen += protocol.ByteCount(quicvarint.Len(uint64(maxLen)))
|
||||
frame, blocked, hasMoreData := str.popStreamFrame(maxLen, v)
|
||||
if hasMoreData { // put the stream back in the queue (at the end)
|
||||
f.streamQueue.PushBack(id)
|
||||
} else { // no more data to send. Stream is not active
|
||||
delete(f.activeStreams, id)
|
||||
}
|
||||
// Note that the frame.Frame can be nil:
|
||||
// * if the stream was canceled after it said it had data
|
||||
// * the remaining size doesn't allow us to add another STREAM frame
|
||||
return frame, blocked
|
||||
}
|
||||
|
||||
func (f *framer) Handle0RTTRejection() {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
f.controlFrameMutex.Lock()
|
||||
defer f.controlFrameMutex.Unlock()
|
||||
|
||||
f.streamQueue.Clear()
|
||||
for id := range f.activeStreams {
|
||||
delete(f.activeStreams, id)
|
||||
@@ -195,16 +279,13 @@ func (f *framerI) Handle0RTTRejection() error {
|
||||
var j int
|
||||
for i, frame := range f.controlFrames {
|
||||
switch frame.(type) {
|
||||
case *wire.MaxDataFrame, *wire.MaxStreamDataFrame, *wire.MaxStreamsFrame:
|
||||
return errors.New("didn't expect MAX_DATA / MAX_STREAM_DATA / MAX_STREAMS frame to be sent in 0-RTT")
|
||||
case *wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame:
|
||||
case *wire.MaxDataFrame, *wire.MaxStreamDataFrame, *wire.MaxStreamsFrame,
|
||||
*wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame:
|
||||
continue
|
||||
default:
|
||||
f.controlFrames[j] = f.controlFrames[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
f.controlFrames = f.controlFrames[:j]
|
||||
f.controlFrameMutex.Unlock()
|
||||
return nil
|
||||
f.controlFrames = slices.Delete(f.controlFrames, j, len(f.controlFrames))
|
||||
}
|
||||
|
||||
131
vendor/github.com/quic-go/quic-go/interface.go
generated
vendored
131
vendor/github.com/quic-go/quic-go/interface.go
generated
vendored
@@ -19,10 +19,6 @@ type StreamID = protocol.StreamID
|
||||
// A Version is a QUIC version number.
|
||||
type Version = protocol.Version
|
||||
|
||||
// A VersionNumber is a QUIC version number.
|
||||
// Deprecated: VersionNumber was renamed to Version.
|
||||
type VersionNumber = Version
|
||||
|
||||
const (
|
||||
// Version1 is RFC 9000
|
||||
Version1 = protocol.Version1
|
||||
@@ -48,31 +44,34 @@ type TokenStore interface {
|
||||
}
|
||||
|
||||
// Err0RTTRejected is the returned from:
|
||||
// * Open{Uni}Stream{Sync}
|
||||
// * Accept{Uni}Stream
|
||||
// * Stream.Read and Stream.Write
|
||||
// - Open{Uni}Stream{Sync}
|
||||
// - Accept{Uni}Stream
|
||||
// - Stream.Read and Stream.Write
|
||||
//
|
||||
// when the server rejects a 0-RTT connection attempt.
|
||||
var Err0RTTRejected = errors.New("0-RTT rejected")
|
||||
|
||||
// ConnectionTracingKey can be used to associate a ConnectionTracer with a Connection.
|
||||
// ConnectionTracingKey can be used to associate a [logging.ConnectionTracer] with a [Connection].
|
||||
// It is set on the Connection.Context() context,
|
||||
// as well as on the context passed to logging.Tracer.NewConnectionTracer.
|
||||
//
|
||||
// Deprecated: Applications can set their own tracing key using Transport.ConnContext.
|
||||
var ConnectionTracingKey = connTracingCtxKey{}
|
||||
|
||||
// ConnectionTracingID is the type of the context value saved under the ConnectionTracingKey.
|
||||
//
|
||||
// Deprecated: Applications can set their own tracing key using Transport.ConnContext.
|
||||
type ConnectionTracingID uint64
|
||||
|
||||
type connTracingCtxKey struct{}
|
||||
|
||||
// QUICVersionContextKey can be used to find out the QUIC version of a TLS handshake from the
|
||||
// context returned by tls.Config.ClientHelloInfo.Context.
|
||||
// context returned by tls.Config.ClientInfo.Context.
|
||||
var QUICVersionContextKey = handshake.QUICVersionContextKey
|
||||
|
||||
// Stream is the interface implemented by QUIC streams
|
||||
// In addition to the errors listed on the Connection,
|
||||
// calls to stream functions can return a StreamError if the stream is canceled.
|
||||
// Stream is the interface implemented by QUIC streams.
|
||||
// In addition to the errors listed on the [Connection],
|
||||
// calls to stream functions can return a [StreamError] if the stream is canceled.
|
||||
type Stream interface {
|
||||
ReceiveStream
|
||||
SendStream
|
||||
@@ -87,12 +86,8 @@ type ReceiveStream interface {
|
||||
// StreamID returns the stream ID.
|
||||
StreamID() StreamID
|
||||
// Read reads data from the stream.
|
||||
// Read can be made to time out and return a net.Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
||||
// If the stream was canceled by the peer, the error implements the StreamError
|
||||
// interface, and Canceled() == true.
|
||||
// If the connection was closed due to a timeout, the error satisfies
|
||||
// the net.Error interface, and Timeout() will be true.
|
||||
// Read can be made to time out using SetDeadline and SetReadDeadline.
|
||||
// If the stream was canceled, the error is a StreamError.
|
||||
io.Reader
|
||||
// CancelRead aborts receiving on this stream.
|
||||
// It will ask the peer to stop transmitting stream data.
|
||||
@@ -102,7 +97,6 @@ type ReceiveStream interface {
|
||||
// SetReadDeadline sets the deadline for future Read calls and
|
||||
// any currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
|
||||
SetReadDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
@@ -111,12 +105,8 @@ type SendStream interface {
|
||||
// StreamID returns the stream ID.
|
||||
StreamID() StreamID
|
||||
// Write writes data to the stream.
|
||||
// Write can be made to time out and return a net.Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
||||
// If the stream was canceled by the peer, the error implements the StreamError
|
||||
// interface, and Canceled() == true.
|
||||
// If the connection was closed due to a timeout, the error satisfies
|
||||
// the net.Error interface, and Timeout() will be true.
|
||||
// Write can be made to time out using SetDeadline and SetWriteDeadline.
|
||||
// If the stream was canceled, the error is a StreamError.
|
||||
io.Writer
|
||||
// Close closes the write-direction of the stream.
|
||||
// Future calls to Write are not permitted after calling Close.
|
||||
@@ -146,45 +136,42 @@ type SendStream interface {
|
||||
|
||||
// A Connection is a QUIC connection between two peers.
|
||||
// Calls to the connection (and to streams) can return the following types of errors:
|
||||
// * ApplicationError: for errors triggered by the application running on top of QUIC
|
||||
// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
|
||||
// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
|
||||
// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
|
||||
// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
|
||||
// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
|
||||
// - [ApplicationError]: for errors triggered by the application running on top of QUIC
|
||||
// - [TransportError]: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
|
||||
// - [IdleTimeoutError]: when the peer goes away unexpectedly (this is a [net.Error] timeout error)
|
||||
// - [HandshakeTimeoutError]: when the cryptographic handshake takes too long (this is a [net.Error] timeout error)
|
||||
// - [StatelessResetError]: when we receive a stateless reset
|
||||
// - [VersionNegotiationError]: returned by the client, when there's no version overlap between the peers
|
||||
type Connection interface {
|
||||
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
|
||||
// If the connection was closed due to a timeout, the error satisfies
|
||||
// the net.Error interface, and Timeout() will be true.
|
||||
AcceptStream(context.Context) (Stream, error)
|
||||
// AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available.
|
||||
// If the connection was closed due to a timeout, the error satisfies
|
||||
// the net.Error interface, and Timeout() will be true.
|
||||
AcceptUniStream(context.Context) (ReceiveStream, error)
|
||||
// OpenStream opens a new bidirectional QUIC stream.
|
||||
// There is no signaling to the peer about new streams:
|
||||
// The peer can only accept the stream after data has been sent on the stream.
|
||||
// If the error is non-nil, it satisfies the net.Error interface.
|
||||
// When reaching the peer's stream limit, err.Temporary() will be true.
|
||||
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||
// The peer can only accept the stream after data has been sent on the stream,
|
||||
// or the stream has been reset or closed.
|
||||
// When reaching the peer's stream limit, it is not possible to open a new stream until the
|
||||
// peer raises the stream limit. In that case, a StreamLimitReachedError is returned.
|
||||
OpenStream() (Stream, error)
|
||||
// OpenStreamSync opens a new bidirectional QUIC stream.
|
||||
// It blocks until a new stream can be opened.
|
||||
// There is no signaling to the peer about new streams:
|
||||
// The peer can only accept the stream after data has been sent on the stream,
|
||||
// or the stream has been reset or closed.
|
||||
// If the error is non-nil, it satisfies the net.Error interface.
|
||||
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||
OpenStreamSync(context.Context) (Stream, error)
|
||||
// OpenUniStream opens a new outgoing unidirectional QUIC stream.
|
||||
// If the error is non-nil, it satisfies the net.Error interface.
|
||||
// When reaching the peer's stream limit, Temporary() will be true.
|
||||
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||
// There is no signaling to the peer about new streams:
|
||||
// The peer can only accept the stream after data has been sent on the stream,
|
||||
// or the stream has been reset or closed.
|
||||
// When reaching the peer's stream limit, it is not possible to open a new stream until the
|
||||
// peer raises the stream limit. In that case, a StreamLimitReachedError is returned.
|
||||
OpenUniStream() (SendStream, error)
|
||||
// OpenUniStreamSync opens a new outgoing unidirectional QUIC stream.
|
||||
// It blocks until a new stream can be opened.
|
||||
// If the error is non-nil, it satisfies the net.Error interface.
|
||||
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||
// There is no signaling to the peer about new streams:
|
||||
// The peer can only accept the stream after data has been sent on the stream,
|
||||
// or the stream has been reset or closed.
|
||||
OpenUniStreamSync(context.Context) (SendStream, error)
|
||||
// LocalAddr returns the local address.
|
||||
LocalAddr() net.Addr
|
||||
@@ -209,6 +196,8 @@ type Connection interface {
|
||||
SendDatagram(payload []byte) error
|
||||
// ReceiveDatagram gets a message received in a datagram, as specified in RFC 9221.
|
||||
ReceiveDatagram(context.Context) ([]byte, error)
|
||||
|
||||
AddPath(*Transport) (*Path, error)
|
||||
}
|
||||
|
||||
// An EarlyConnection is a connection that is handshaking.
|
||||
@@ -238,27 +227,22 @@ type TokenGeneratorKey = handshake.TokenProtectorKey
|
||||
// as they are allowed by RFC 8999.
|
||||
type ConnectionID = protocol.ConnectionID
|
||||
|
||||
// ConnectionIDFromBytes interprets b as a Connection ID. It panics if b is
|
||||
// ConnectionIDFromBytes interprets b as a [ConnectionID]. It panics if b is
|
||||
// longer than 20 bytes.
|
||||
func ConnectionIDFromBytes(b []byte) ConnectionID {
|
||||
return protocol.ParseConnectionID(b)
|
||||
}
|
||||
|
||||
// A ConnectionIDGenerator is an interface that allows clients to implement their own format
|
||||
// for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets.
|
||||
//
|
||||
// Connection IDs generated by an implementation should always produce IDs of constant size.
|
||||
// A ConnectionIDGenerator allows the application to take control over the generation of Connection IDs.
|
||||
// Connection IDs generated by an implementation must be of constant length.
|
||||
type ConnectionIDGenerator interface {
|
||||
// GenerateConnectionID generates a new ConnectionID.
|
||||
// Generated ConnectionIDs should be unique and observers should not be able to correlate two ConnectionIDs.
|
||||
// GenerateConnectionID generates a new Connection ID.
|
||||
// Generated Connection IDs must be unique and observers should not be able to correlate two Connection IDs.
|
||||
GenerateConnectionID() (ConnectionID, error)
|
||||
|
||||
// ConnectionIDLen tells what is the length of the ConnectionIDs generated by the implementation of
|
||||
// this interface.
|
||||
// Effectively, this means that implementations of ConnectionIDGenerator must always return constant-size
|
||||
// connection IDs. Valid lengths are between 0 and 20 and calls to GenerateConnectionID.
|
||||
// 0-length ConnectionsIDs can be used when an endpoint (server or client) does not require multiplexing connections
|
||||
// in the presence of a connection migration environment.
|
||||
// ConnectionIDLen returns the length of Connection IDs generated by this implementation.
|
||||
// Implementations must return constant-length Connection IDs with lengths between 0 and 20 bytes.
|
||||
// A length of 0 can only be used when an endpoint doesn't need to multiplex connections during migration.
|
||||
ConnectionIDLen() int
|
||||
}
|
||||
|
||||
@@ -266,7 +250,7 @@ type ConnectionIDGenerator interface {
|
||||
type Config struct {
|
||||
// GetConfigForClient is called for incoming connections.
|
||||
// If the error is not nil, the connection attempt is refused.
|
||||
GetConfigForClient func(info *ClientHelloInfo) (*Config, error)
|
||||
GetConfigForClient func(info *ClientInfo) (*Config, error)
|
||||
// The QUIC versions that can be negotiated.
|
||||
// If not set, it uses all versions available.
|
||||
Versions []Version
|
||||
@@ -327,10 +311,10 @@ type Config struct {
|
||||
// If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most
|
||||
// every half of MaxIdleTimeout, whichever is smaller).
|
||||
KeepAlivePeriod time.Duration
|
||||
// InitialPacketSize is the initial size of packets sent.
|
||||
// It is usually not necessary to manually set this value,
|
||||
// since Path MTU discovery very quickly finds the path's MTU.
|
||||
// If set too high, the path might not support packets that large, leading to a timeout of the QUIC handshake.
|
||||
// InitialPacketSize is the initial size (and the lower limit) for packets sent.
|
||||
// Under most circumstances, it is not necessary to manually set this value,
|
||||
// since path MTU discovery quickly finds the path's MTU.
|
||||
// If set too high, the path might not support packets of that size, leading to a timeout of the QUIC handshake.
|
||||
// Values below 1200 are invalid.
|
||||
InitialPacketSize uint16
|
||||
// DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899).
|
||||
@@ -346,7 +330,12 @@ type Config struct {
|
||||
}
|
||||
|
||||
// ClientHelloInfo contains information about an incoming connection attempt.
|
||||
type ClientHelloInfo struct {
|
||||
//
|
||||
// Deprecated: Use ClientInfo instead.
|
||||
type ClientHelloInfo = ClientInfo
|
||||
|
||||
// ClientInfo contains information about an incoming connection attempt.
|
||||
type ClientInfo struct {
|
||||
// RemoteAddr is the remote address on the Initial packet.
|
||||
// Unless AddrVerified is set, the address is not yet verified, and could be a spoofed IP address.
|
||||
RemoteAddr net.Addr
|
||||
@@ -356,19 +345,19 @@ type ClientHelloInfo struct {
|
||||
AddrVerified bool
|
||||
}
|
||||
|
||||
// ConnectionState records basic details about a QUIC connection
|
||||
// ConnectionState records basic details about a QUIC connection.
|
||||
type ConnectionState struct {
|
||||
// TLS contains information about the TLS connection state, incl. the tls.ConnectionState.
|
||||
TLS tls.ConnectionState
|
||||
// SupportsDatagrams says if support for QUIC datagrams (RFC 9221) was negotiated.
|
||||
// This requires both nodes to support and enable the datagram extensions (via Config.EnableDatagrams).
|
||||
// If datagram support was negotiated, datagrams can be sent and received using the
|
||||
// SendDatagram and ReceiveDatagram methods on the Connection.
|
||||
// SupportsDatagrams indicates whether the peer advertised support for QUIC datagrams (RFC 9221).
|
||||
// When true, datagrams can be sent using the Connection's SendDatagram method.
|
||||
// This is a unilateral declaration by the peer - receiving datagrams is only possible if
|
||||
// datagram support was enabled locally via Config.EnableDatagrams.
|
||||
SupportsDatagrams bool
|
||||
// Used0RTT says if 0-RTT resumption was used.
|
||||
Used0RTT bool
|
||||
// Version is the QUIC version of the QUIC connection.
|
||||
Version Version
|
||||
// GSO says if generic segmentation offload is used
|
||||
// GSO says if generic segmentation offload is used.
|
||||
GSO bool
|
||||
}
|
||||
|
||||
17
vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go
generated
vendored
17
vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go
generated
vendored
@@ -10,14 +10,13 @@ import (
|
||||
// SentPacketHandler handles ACKs received for outgoing packets
|
||||
type SentPacketHandler interface {
|
||||
// SentPacket may modify the packet
|
||||
SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool)
|
||||
SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket, isPathProbePacket bool)
|
||||
// ReceivedAck processes an ACK frame.
|
||||
// It does not store a copy of the frame.
|
||||
ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* 1-RTT packet acked */, error)
|
||||
ReceivedBytes(protocol.ByteCount)
|
||||
DropPackets(protocol.EncryptionLevel)
|
||||
ResetForRetry(rcvTime time.Time) error
|
||||
SetHandshakeConfirmed()
|
||||
ReceivedBytes(_ protocol.ByteCount, rcvTime time.Time)
|
||||
DropPackets(_ protocol.EncryptionLevel, rcvTime time.Time)
|
||||
ResetForRetry(rcvTime time.Time)
|
||||
|
||||
// The SendMode determines if and what kind of packets can be sent.
|
||||
SendMode(now time.Time) SendMode
|
||||
@@ -34,12 +33,14 @@ type SentPacketHandler interface {
|
||||
PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber
|
||||
|
||||
GetLossDetectionTimeout() time.Time
|
||||
OnLossDetectionTimeout() error
|
||||
OnLossDetectionTimeout(now time.Time) error
|
||||
|
||||
MigratedPath(now time.Time, initialMaxPacketSize protocol.ByteCount)
|
||||
}
|
||||
|
||||
type sentPacketTracker interface {
|
||||
GetLowestPacketNotConfirmedAcked() protocol.PacketNumber
|
||||
ReceivedPacket(protocol.EncryptionLevel)
|
||||
ReceivedPacket(_ protocol.EncryptionLevel, rcvTime time.Time)
|
||||
}
|
||||
|
||||
// ReceivedPacketHandler handles ACKs needed to send for incoming packets
|
||||
@@ -49,5 +50,5 @@ type ReceivedPacketHandler interface {
|
||||
DropPackets(protocol.EncryptionLevel)
|
||||
|
||||
GetAlarmTimeout() time.Time
|
||||
GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame
|
||||
GetAckFrame(_ protocol.EncryptionLevel, now time.Time, onlyIfQueued bool) *wire.AckFrame
|
||||
}
|
||||
|
||||
3
vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go
generated
vendored
3
vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go
generated
vendored
@@ -22,10 +22,11 @@ type packet struct {
|
||||
includedInBytesInFlight bool
|
||||
declaredLost bool
|
||||
skippedPacket bool
|
||||
isPathProbePacket bool
|
||||
}
|
||||
|
||||
func (p *packet) outstanding() bool {
|
||||
return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket
|
||||
return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket && !p.isPathProbePacket
|
||||
}
|
||||
|
||||
var packetPool = sync.Pool{New: func() any { return &packet{} }}
|
||||
|
||||
6
vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go
generated
vendored
6
vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go
generated
vendored
@@ -38,7 +38,7 @@ func (h *receivedPacketHandler) ReceivedPacket(
|
||||
rcvTime time.Time,
|
||||
ackEliciting bool,
|
||||
) error {
|
||||
h.sentPackets.ReceivedPacket(encLevel)
|
||||
h.sentPackets.ReceivedPacket(encLevel, rcvTime)
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting)
|
||||
@@ -87,7 +87,7 @@ func (h *receivedPacketHandler) GetAlarmTimeout() time.Time {
|
||||
return h.appDataPackets.GetAlarmTimeout()
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame {
|
||||
func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, now time.Time, onlyIfQueued bool) *wire.AckFrame {
|
||||
//nolint:exhaustive // 0-RTT packets can't contain ACK frames.
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
@@ -101,7 +101,7 @@ func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, o
|
||||
}
|
||||
return nil
|
||||
case protocol.Encryption1RTT:
|
||||
return h.appDataPackets.GetAckFrame(onlyIfQueued)
|
||||
return h.appDataPackets.GetAckFrame(now, onlyIfQueued)
|
||||
default:
|
||||
// 0-RTT packets can't contain ACK frames
|
||||
return nil
|
||||
|
||||
104
vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go
generated
vendored
104
vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go
generated
vendored
@@ -1,10 +1,9 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"slices"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
list "github.com/quic-go/quic-go/internal/utils/linkedlist"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
@@ -14,25 +13,17 @@ type interval struct {
|
||||
End protocol.PacketNumber
|
||||
}
|
||||
|
||||
var intervalElementPool sync.Pool
|
||||
|
||||
func init() {
|
||||
intervalElementPool = *list.NewPool[interval]()
|
||||
}
|
||||
|
||||
// The receivedPacketHistory stores if a packet number has already been received.
|
||||
// It generates ACK ranges which can be used to assemble an ACK frame.
|
||||
// It does not store packet contents.
|
||||
type receivedPacketHistory struct {
|
||||
ranges *list.List[interval]
|
||||
ranges []interval // maximum length: protocol.MaxNumAckRanges
|
||||
|
||||
deletedBelow protocol.PacketNumber
|
||||
}
|
||||
|
||||
func newReceivedPacketHistory() *receivedPacketHistory {
|
||||
return &receivedPacketHistory{
|
||||
ranges: list.NewWithPool[interval](&intervalElementPool),
|
||||
}
|
||||
return &receivedPacketHistory{}
|
||||
}
|
||||
|
||||
// ReceivedPacket registers a packet with PacketNumber p and updates the ranges
|
||||
@@ -41,58 +32,54 @@ func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) bool /*
|
||||
if p < h.deletedBelow {
|
||||
return false
|
||||
}
|
||||
|
||||
isNew := h.addToRanges(p)
|
||||
h.maybeDeleteOldRanges()
|
||||
// Delete old ranges, if we're tracking too many of them.
|
||||
// This is a DoS defense against a peer that sends us too many gaps.
|
||||
if len(h.ranges) > protocol.MaxNumAckRanges {
|
||||
h.ranges = slices.Delete(h.ranges, 0, len(h.ranges)-protocol.MaxNumAckRanges)
|
||||
}
|
||||
return isNew
|
||||
}
|
||||
|
||||
func (h *receivedPacketHistory) addToRanges(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ {
|
||||
if h.ranges.Len() == 0 {
|
||||
h.ranges.PushBack(interval{Start: p, End: p})
|
||||
if len(h.ranges) == 0 {
|
||||
h.ranges = append(h.ranges, interval{Start: p, End: p})
|
||||
return true
|
||||
}
|
||||
|
||||
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||
for i := len(h.ranges) - 1; i >= 0; i-- {
|
||||
// p already included in an existing range. Nothing to do here
|
||||
if p >= el.Value.Start && p <= el.Value.End {
|
||||
if p >= h.ranges[i].Start && p <= h.ranges[i].End {
|
||||
return false
|
||||
}
|
||||
|
||||
if el.Value.End == p-1 { // extend a range at the end
|
||||
el.Value.End = p
|
||||
if h.ranges[i].End == p-1 { // extend a range at the end
|
||||
h.ranges[i].End = p
|
||||
return true
|
||||
}
|
||||
if el.Value.Start == p+1 { // extend a range at the beginning
|
||||
el.Value.Start = p
|
||||
if h.ranges[i].Start == p+1 { // extend a range at the beginning
|
||||
h.ranges[i].Start = p
|
||||
|
||||
prev := el.Prev()
|
||||
if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges
|
||||
prev.Value.End = el.Value.End
|
||||
h.ranges.Remove(el)
|
||||
if i > 0 && h.ranges[i-1].End+1 == h.ranges[i].Start { // merge two ranges
|
||||
h.ranges[i-1].End = h.ranges[i].End
|
||||
h.ranges = slices.Delete(h.ranges, i, i+1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// create a new range at the end
|
||||
if p > el.Value.End {
|
||||
h.ranges.InsertAfter(interval{Start: p, End: p}, el)
|
||||
// create a new range after the current one
|
||||
if p > h.ranges[i].End {
|
||||
h.ranges = slices.Insert(h.ranges, i+1, interval{Start: p, End: p})
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// create a new range at the beginning
|
||||
h.ranges.InsertBefore(interval{Start: p, End: p}, h.ranges.Front())
|
||||
h.ranges = slices.Insert(h.ranges, 0, interval{Start: p, End: p})
|
||||
return true
|
||||
}
|
||||
|
||||
// Delete old ranges, if we're tracking more than 500 of them.
|
||||
// This is a DoS defense against a peer that sends us too many gaps.
|
||||
func (h *receivedPacketHistory) maybeDeleteOldRanges() {
|
||||
for h.ranges.Len() > protocol.MaxNumAckRanges {
|
||||
h.ranges.Remove(h.ranges.Front())
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteBelow deletes all entries below (but not including) p
|
||||
func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) {
|
||||
if p < h.deletedBelow {
|
||||
@@ -100,37 +87,39 @@ func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) {
|
||||
}
|
||||
h.deletedBelow = p
|
||||
|
||||
nextEl := h.ranges.Front()
|
||||
for el := h.ranges.Front(); nextEl != nil; el = nextEl {
|
||||
nextEl = el.Next()
|
||||
if len(h.ranges) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if el.Value.End < p { // delete a whole range
|
||||
h.ranges.Remove(el)
|
||||
} else if p > el.Value.Start && p <= el.Value.End {
|
||||
el.Value.Start = p
|
||||
return
|
||||
idx := -1
|
||||
for i := 0; i < len(h.ranges); i++ {
|
||||
if h.ranges[i].End < p { // delete a whole range
|
||||
idx = i
|
||||
} else if p > h.ranges[i].Start && p <= h.ranges[i].End {
|
||||
h.ranges[i].Start = p
|
||||
break
|
||||
} else { // no ranges affected. Nothing to do
|
||||
return
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx >= 0 {
|
||||
h.ranges = slices.Delete(h.ranges, 0, idx+1)
|
||||
}
|
||||
}
|
||||
|
||||
// AppendAckRanges appends to a slice of all AckRanges that can be used in an AckFrame
|
||||
func (h *receivedPacketHistory) AppendAckRanges(ackRanges []wire.AckRange) []wire.AckRange {
|
||||
if h.ranges.Len() > 0 {
|
||||
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||
ackRanges = append(ackRanges, wire.AckRange{Smallest: el.Value.Start, Largest: el.Value.End})
|
||||
}
|
||||
for i := len(h.ranges) - 1; i >= 0; i-- {
|
||||
ackRanges = append(ackRanges, wire.AckRange{Smallest: h.ranges[i].Start, Largest: h.ranges[i].End})
|
||||
}
|
||||
return ackRanges
|
||||
}
|
||||
|
||||
func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange {
|
||||
ackRange := wire.AckRange{}
|
||||
if h.ranges.Len() > 0 {
|
||||
r := h.ranges.Back().Value
|
||||
ackRange.Smallest = r.Start
|
||||
ackRange.Largest = r.End
|
||||
if len(h.ranges) > 0 {
|
||||
ackRange.Smallest = h.ranges[len(h.ranges)-1].Start
|
||||
ackRange.Largest = h.ranges[len(h.ranges)-1].End
|
||||
}
|
||||
return ackRange
|
||||
}
|
||||
@@ -139,11 +128,12 @@ func (h *receivedPacketHistory) IsPotentiallyDuplicate(p protocol.PacketNumber)
|
||||
if p < h.deletedBelow {
|
||||
return true
|
||||
}
|
||||
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||
if p > el.Value.End {
|
||||
// Iterating over the slices is faster than using a binary search (using slices.BinarySearchFunc).
|
||||
for i := len(h.ranges) - 1; i >= 0; i-- {
|
||||
if p > h.ranges[i].End {
|
||||
return false
|
||||
}
|
||||
if p <= el.Value.End && p >= el.Value.Start {
|
||||
if p <= h.ranges[i].End && p >= h.ranges[i].Start {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
3
vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go
generated
vendored
3
vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go
generated
vendored
@@ -196,8 +196,7 @@ func (h *appDataReceivedPacketTracker) shouldQueueACK(pn protocol.PacketNumber,
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *appDataReceivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame {
|
||||
now := time.Now()
|
||||
func (h *appDataReceivedPacketTracker) GetAckFrame(now time.Time, onlyIfQueued bool) *wire.AckFrame {
|
||||
if onlyIfQueued && !h.ackQueued {
|
||||
if h.ackAlarm.IsZero() || h.ackAlarm.After(now) {
|
||||
return nil
|
||||
|
||||
362
vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go
generated
vendored
362
vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go
generated
vendored
@@ -27,8 +27,11 @@ const (
|
||||
maxPTODuration = 60 * time.Second
|
||||
)
|
||||
|
||||
// Path probe packets are declared lost after this time.
|
||||
const pathProbePacketLossTimeout = time.Second
|
||||
|
||||
type packetNumberSpace struct {
|
||||
history *sentPacketHistory
|
||||
history sentPacketHistory
|
||||
pns packetNumberGenerator
|
||||
|
||||
lossTime time.Time
|
||||
@@ -38,21 +41,27 @@ type packetNumberSpace struct {
|
||||
largestSent protocol.PacketNumber
|
||||
}
|
||||
|
||||
func newPacketNumberSpace(initialPN protocol.PacketNumber, skipPNs bool) *packetNumberSpace {
|
||||
func newPacketNumberSpace(initialPN protocol.PacketNumber, isAppData bool) *packetNumberSpace {
|
||||
var pns packetNumberGenerator
|
||||
if skipPNs {
|
||||
if isAppData {
|
||||
pns = newSkippingPacketNumberGenerator(initialPN, protocol.SkipPacketInitialPeriod, protocol.SkipPacketMaxPeriod)
|
||||
} else {
|
||||
pns = newSequentialPacketNumberGenerator(initialPN)
|
||||
}
|
||||
return &packetNumberSpace{
|
||||
history: newSentPacketHistory(),
|
||||
history: *newSentPacketHistory(isAppData),
|
||||
pns: pns,
|
||||
largestSent: protocol.InvalidPacketNumber,
|
||||
largestAcked: protocol.InvalidPacketNumber,
|
||||
}
|
||||
}
|
||||
|
||||
type alarmTimer struct {
|
||||
Time time.Time
|
||||
TimerType logging.TimerType
|
||||
EncryptionLevel protocol.EncryptionLevel
|
||||
}
|
||||
|
||||
type sentPacketHandler struct {
|
||||
initialPackets *packetNumberSpace
|
||||
handshakePackets *packetNumberSpace
|
||||
@@ -90,7 +99,7 @@ type sentPacketHandler struct {
|
||||
numProbesToSend int
|
||||
|
||||
// The alarm timeout
|
||||
alarm time.Time
|
||||
alarm alarmTimer
|
||||
|
||||
enableECN bool
|
||||
ecnTracker ecnHandler
|
||||
@@ -155,7 +164,7 @@ func (h *sentPacketHandler) removeFromBytesInFlight(p *packet) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {
|
||||
func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel, now time.Time) {
|
||||
// The server won't await address validation after the handshake is confirmed.
|
||||
// This applies even if we didn't receive an ACK for a Handshake packet.
|
||||
if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionHandshake {
|
||||
@@ -168,10 +177,9 @@ func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {
|
||||
if pnSpace == nil {
|
||||
return
|
||||
}
|
||||
pnSpace.history.Iterate(func(p *packet) (bool, error) {
|
||||
for p := range pnSpace.history.Packets() {
|
||||
h.removeFromBytesInFlight(p)
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
// drop the packet history
|
||||
//nolint:exhaustive // Not every packet number space can be dropped.
|
||||
@@ -179,20 +187,22 @@ func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {
|
||||
case protocol.EncryptionInitial:
|
||||
h.initialPackets = nil
|
||||
case protocol.EncryptionHandshake:
|
||||
// Dropping the handshake packet number space means that the handshake is confirmed,
|
||||
// see section 4.9.2 of RFC 9001.
|
||||
h.handshakeConfirmed = true
|
||||
h.handshakePackets = nil
|
||||
case protocol.Encryption0RTT:
|
||||
// This function is only called when 0-RTT is rejected,
|
||||
// and not when the client drops 0-RTT keys when the handshake completes.
|
||||
// When 0-RTT is rejected, all application data sent so far becomes invalid.
|
||||
// Delete the packets from the history and remove them from bytes_in_flight.
|
||||
h.appDataPackets.history.Iterate(func(p *packet) (bool, error) {
|
||||
for p := range h.appDataPackets.history.Packets() {
|
||||
if p.EncryptionLevel != protocol.Encryption0RTT && !p.skippedPacket {
|
||||
return false, nil
|
||||
break
|
||||
}
|
||||
h.removeFromBytesInFlight(p)
|
||||
h.appDataPackets.history.Remove(p.PacketNumber)
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel))
|
||||
}
|
||||
@@ -202,21 +212,21 @@ func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {
|
||||
h.ptoCount = 0
|
||||
h.numProbesToSend = 0
|
||||
h.ptoMode = SendNone
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(now)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) {
|
||||
func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount, t time.Time) {
|
||||
wasAmplificationLimit := h.isAmplificationLimited()
|
||||
h.bytesReceived += n
|
||||
if wasAmplificationLimit && !h.isAmplificationLimited() {
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) {
|
||||
func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel, t time.Time) {
|
||||
if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated {
|
||||
h.peerAddressValidated = true
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,11 +250,12 @@ func (h *sentPacketHandler) SentPacket(
|
||||
ecn protocol.ECN,
|
||||
size protocol.ByteCount,
|
||||
isPathMTUProbePacket bool,
|
||||
isPathProbePacket bool,
|
||||
) {
|
||||
h.bytesSent += size
|
||||
|
||||
pnSpace := h.getPacketNumberSpace(encLevel)
|
||||
if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() {
|
||||
if h.logger.Debug() && (pnSpace.history.HasOutstandingPackets() || pnSpace.history.HasOutstandingPathProbes()) {
|
||||
for p := max(0, pnSpace.largestSent+1); p < pn; p++ {
|
||||
h.logger.Debugf("Skipping packet number %d", p)
|
||||
}
|
||||
@@ -253,6 +264,18 @@ func (h *sentPacketHandler) SentPacket(
|
||||
pnSpace.largestSent = pn
|
||||
isAckEliciting := len(streamFrames) > 0 || len(frames) > 0
|
||||
|
||||
if isPathProbePacket {
|
||||
p := getPacket()
|
||||
p.SendTime = t
|
||||
p.PacketNumber = pn
|
||||
p.EncryptionLevel = encLevel
|
||||
p.Length = size
|
||||
p.Frames = frames
|
||||
p.isPathProbePacket = true
|
||||
pnSpace.history.SentPathProbePacket(p)
|
||||
h.setLossDetectionTimer(t)
|
||||
return
|
||||
}
|
||||
if isAckEliciting {
|
||||
pnSpace.lastAckElicitingPacketTime = t
|
||||
h.bytesInFlight += size
|
||||
@@ -269,7 +292,7 @@ func (h *sentPacketHandler) SentPacket(
|
||||
if !isAckEliciting {
|
||||
pnSpace.history.SentNonAckElicitingPacket(pn)
|
||||
if !h.peerCompletedAddressValidation {
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(t)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -289,7 +312,7 @@ func (h *sentPacketHandler) SentPacket(
|
||||
if h.tracer != nil && h.tracer.UpdatedMetrics != nil {
|
||||
h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight())
|
||||
}
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(t)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace {
|
||||
@@ -322,7 +345,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
h.peerCompletedAddressValidation = true
|
||||
h.logger.Debugf("Peer doesn't await address validation any longer.")
|
||||
// Make sure that the timer is reset, even if this ACK doesn't acknowledge any (ack-eliciting) packets.
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(rcvTime)
|
||||
}
|
||||
|
||||
priorInFlight := h.bytesInFlight
|
||||
@@ -332,13 +355,13 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
}
|
||||
// update the RTT, if the largest acked is newly acknowledged
|
||||
if len(ackedPackets) > 0 {
|
||||
if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() {
|
||||
if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() && !p.isPathProbePacket {
|
||||
// don't use the ack delay for Initial and Handshake packets
|
||||
var ackDelay time.Duration
|
||||
if encLevel == protocol.Encryption1RTT {
|
||||
ackDelay = min(ack.DelayTime, h.rttStats.MaxAckDelay())
|
||||
}
|
||||
h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackDelay, rcvTime)
|
||||
h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackDelay)
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
|
||||
}
|
||||
@@ -356,8 +379,9 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
|
||||
pnSpace.largestAcked = max(pnSpace.largestAcked, largestAcked)
|
||||
|
||||
if err := h.detectLostPackets(rcvTime, encLevel); err != nil {
|
||||
return false, err
|
||||
h.detectLostPackets(rcvTime, encLevel)
|
||||
if encLevel == protocol.Encryption1RTT {
|
||||
h.detectLostPathProbes(rcvTime)
|
||||
}
|
||||
var acked1RTTPacket bool
|
||||
for _, p := range ackedPackets {
|
||||
@@ -368,7 +392,9 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
acked1RTTPacket = true
|
||||
}
|
||||
h.removeFromBytesInFlight(p)
|
||||
putPacket(p)
|
||||
if !p.isPathProbePacket {
|
||||
putPacket(p)
|
||||
}
|
||||
}
|
||||
// After this point, we must not use ackedPackets any longer!
|
||||
// We've already returned the buffers.
|
||||
@@ -387,7 +413,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight())
|
||||
}
|
||||
|
||||
h.setLossDetectionTimer()
|
||||
h.setLossDetectionTimer(rcvTime)
|
||||
return acked1RTTPacket, nil
|
||||
}
|
||||
|
||||
@@ -402,14 +428,13 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
||||
ackRangeIndex := 0
|
||||
lowestAcked := ack.LowestAcked()
|
||||
largestAcked := ack.LargestAcked()
|
||||
err := pnSpace.history.Iterate(func(p *packet) (bool, error) {
|
||||
// Ignore packets below the lowest acked
|
||||
for p := range pnSpace.history.Packets() {
|
||||
// ignore packets below the lowest acked
|
||||
if p.PacketNumber < lowestAcked {
|
||||
return true, nil
|
||||
continue
|
||||
}
|
||||
// Break after largest acked is reached
|
||||
if p.PacketNumber > largestAcked {
|
||||
return false, nil
|
||||
break
|
||||
}
|
||||
|
||||
if ack.HasMissingRanges() {
|
||||
@@ -421,21 +446,28 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
||||
}
|
||||
|
||||
if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range
|
||||
return true, nil
|
||||
continue
|
||||
}
|
||||
if p.PacketNumber > ackRange.Largest {
|
||||
return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
|
||||
return nil, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
|
||||
}
|
||||
}
|
||||
if p.skippedPacket {
|
||||
return false, &qerr.TransportError{
|
||||
return nil, &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel),
|
||||
}
|
||||
}
|
||||
if p.isPathProbePacket {
|
||||
probePacket := pnSpace.history.RemovePathProbe(p.PacketNumber)
|
||||
// the probe packet might already have been declared lost
|
||||
if probePacket != nil {
|
||||
h.ackedPackets = append(h.ackedPackets, probePacket)
|
||||
}
|
||||
continue
|
||||
}
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
if h.logger.Debug() && len(h.ackedPackets) > 0 {
|
||||
pns := make([]protocol.PacketNumber, len(h.ackedPackets))
|
||||
for i, p := range h.ackedPackets {
|
||||
@@ -466,8 +498,7 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
||||
h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber)
|
||||
}
|
||||
}
|
||||
|
||||
return h.ackedPackets, err
|
||||
return h.ackedPackets, nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
|
||||
@@ -498,41 +529,44 @@ func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration
|
||||
}
|
||||
|
||||
// same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime
|
||||
func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) {
|
||||
func (h *sentPacketHandler) getPTOTimeAndSpace(now time.Time) (pto time.Time, encLevel protocol.EncryptionLevel) {
|
||||
// We only send application data probe packets once the handshake is confirmed,
|
||||
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
|
||||
if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() {
|
||||
if h.peerCompletedAddressValidation {
|
||||
return
|
||||
}
|
||||
t := time.Now().Add(h.getScaledPTO(false))
|
||||
t := now.Add(h.getScaledPTO(false))
|
||||
if h.initialPackets != nil {
|
||||
return t, protocol.EncryptionInitial, true
|
||||
return t, protocol.EncryptionInitial
|
||||
}
|
||||
return t, protocol.EncryptionHandshake, true
|
||||
return t, protocol.EncryptionHandshake
|
||||
}
|
||||
|
||||
if h.initialPackets != nil {
|
||||
if h.initialPackets != nil && h.initialPackets.history.HasOutstandingPackets() &&
|
||||
!h.initialPackets.lastAckElicitingPacketTime.IsZero() {
|
||||
encLevel = protocol.EncryptionInitial
|
||||
if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
|
||||
pto = t.Add(h.getScaledPTO(false))
|
||||
}
|
||||
}
|
||||
if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() {
|
||||
if h.handshakePackets != nil && h.handshakePackets.history.HasOutstandingPackets() &&
|
||||
!h.handshakePackets.lastAckElicitingPacketTime.IsZero() {
|
||||
t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(false))
|
||||
if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
|
||||
pto = t
|
||||
encLevel = protocol.EncryptionHandshake
|
||||
}
|
||||
}
|
||||
if h.handshakeConfirmed && !h.appDataPackets.lastAckElicitingPacketTime.IsZero() {
|
||||
if h.handshakeConfirmed && h.appDataPackets.history.HasOutstandingPackets() &&
|
||||
!h.appDataPackets.lastAckElicitingPacketTime.IsZero() {
|
||||
t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(true))
|
||||
if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
|
||||
pto = t
|
||||
encLevel = protocol.Encryption1RTT
|
||||
}
|
||||
}
|
||||
return pto, encLevel, true
|
||||
return pto, encLevel
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
@@ -545,65 +579,91 @@ func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingPackets() bool {
|
||||
return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) setLossDetectionTimer() {
|
||||
func (h *sentPacketHandler) setLossDetectionTimer(now time.Time) {
|
||||
oldAlarm := h.alarm // only needed in case tracing is enabled
|
||||
lossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !lossTime.IsZero() {
|
||||
// Early retransmit timer or time loss detection.
|
||||
h.alarm = lossTime
|
||||
if h.tracer != nil && h.tracer.SetLossTimer != nil && h.alarm != oldAlarm {
|
||||
h.tracer.SetLossTimer(logging.TimerTypeACK, encLevel, h.alarm)
|
||||
newAlarm := h.lossDetectionTime(now)
|
||||
h.alarm = newAlarm
|
||||
|
||||
hasAlarm := !newAlarm.Time.IsZero()
|
||||
if !hasAlarm && !oldAlarm.Time.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer.")
|
||||
if h.tracer != nil && h.tracer.LossTimerCanceled != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel the alarm if amplification limited.
|
||||
if h.isAmplificationLimited() {
|
||||
h.alarm = time.Time{}
|
||||
if !oldAlarm.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer. Amplification limited.")
|
||||
if h.tracer != nil && h.tracer.LossTimerCanceled != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel the alarm if no packets are outstanding
|
||||
if !h.hasOutstandingPackets() && h.peerCompletedAddressValidation {
|
||||
h.alarm = time.Time{}
|
||||
if !oldAlarm.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
|
||||
if h.tracer != nil && h.tracer.LossTimerCanceled != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PTO alarm
|
||||
ptoTime, encLevel, ok := h.getPTOTimeAndSpace()
|
||||
if !ok {
|
||||
if !oldAlarm.IsZero() {
|
||||
h.alarm = time.Time{}
|
||||
h.logger.Debugf("Canceling loss detection timer. No PTO needed..")
|
||||
if h.tracer != nil && h.tracer.LossTimerCanceled != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
h.alarm = ptoTime
|
||||
if h.tracer != nil && h.tracer.SetLossTimer != nil && h.alarm != oldAlarm {
|
||||
h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm)
|
||||
if hasAlarm && h.tracer != nil && h.tracer.SetLossTimer != nil && newAlarm != oldAlarm {
|
||||
h.tracer.SetLossTimer(newAlarm.TimerType, newAlarm.EncryptionLevel, newAlarm.Time)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.EncryptionLevel) error {
|
||||
func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer {
|
||||
// cancel the alarm if no packets are outstanding
|
||||
if h.peerCompletedAddressValidation && !h.hasOutstandingCryptoPackets() &&
|
||||
!h.appDataPackets.history.HasOutstandingPackets() && !h.appDataPackets.history.HasOutstandingPathProbes() {
|
||||
return alarmTimer{}
|
||||
}
|
||||
|
||||
// cancel the alarm if amplification limited
|
||||
if h.isAmplificationLimited() {
|
||||
return alarmTimer{}
|
||||
}
|
||||
|
||||
var pathProbeLossTime time.Time
|
||||
if h.appDataPackets.history.HasOutstandingPathProbes() {
|
||||
if p := h.appDataPackets.history.FirstOutstandingPathProbe(); p != nil {
|
||||
pathProbeLossTime = p.SendTime.Add(pathProbePacketLossTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// early retransmit timer or time loss detection
|
||||
lossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !lossTime.IsZero() && (pathProbeLossTime.IsZero() || lossTime.Before(pathProbeLossTime)) {
|
||||
return alarmTimer{
|
||||
Time: lossTime,
|
||||
TimerType: logging.TimerTypeACK,
|
||||
EncryptionLevel: encLevel,
|
||||
}
|
||||
}
|
||||
ptoTime, encLevel := h.getPTOTimeAndSpace(now)
|
||||
if !ptoTime.IsZero() && (pathProbeLossTime.IsZero() || ptoTime.Before(pathProbeLossTime)) {
|
||||
return alarmTimer{
|
||||
Time: ptoTime,
|
||||
TimerType: logging.TimerTypePTO,
|
||||
EncryptionLevel: encLevel,
|
||||
}
|
||||
}
|
||||
if !pathProbeLossTime.IsZero() {
|
||||
return alarmTimer{
|
||||
Time: pathProbeLossTime,
|
||||
TimerType: logging.TimerTypePathProbe,
|
||||
EncryptionLevel: protocol.Encryption1RTT,
|
||||
}
|
||||
}
|
||||
return alarmTimer{}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) detectLostPathProbes(now time.Time) {
|
||||
if !h.appDataPackets.history.HasOutstandingPathProbes() {
|
||||
return
|
||||
}
|
||||
lossTime := now.Add(-pathProbePacketLossTimeout)
|
||||
// RemovePathProbe cannot be called while iterating.
|
||||
var lostPathProbes []*packet
|
||||
for p := range h.appDataPackets.history.PathProbes() {
|
||||
if !p.SendTime.After(lossTime) {
|
||||
lostPathProbes = append(lostPathProbes, p)
|
||||
}
|
||||
}
|
||||
for _, p := range lostPathProbes {
|
||||
for _, f := range p.Frames {
|
||||
f.Handler.OnLost(f.Frame)
|
||||
}
|
||||
h.appDataPackets.history.RemovePathProbe(p.PacketNumber)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.EncryptionLevel) {
|
||||
pnSpace := h.getPacketNumberSpace(encLevel)
|
||||
pnSpace.lossTime = time.Time{}
|
||||
|
||||
@@ -617,15 +677,16 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
||||
lostSendTime := now.Add(-lossDelay)
|
||||
|
||||
priorInFlight := h.bytesInFlight
|
||||
return pnSpace.history.Iterate(func(p *packet) (bool, error) {
|
||||
for p := range pnSpace.history.Packets() {
|
||||
if p.PacketNumber > pnSpace.largestAcked {
|
||||
return false, nil
|
||||
break
|
||||
}
|
||||
|
||||
isRegularPacket := !p.skippedPacket && !p.isPathProbePacket
|
||||
var packetLost bool
|
||||
if p.SendTime.Before(lostSendTime) {
|
||||
if !p.SendTime.After(lostSendTime) {
|
||||
packetLost = true
|
||||
if !p.skippedPacket {
|
||||
if isRegularPacket {
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber)
|
||||
}
|
||||
@@ -635,7 +696,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
||||
}
|
||||
} else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold {
|
||||
packetLost = true
|
||||
if !p.skippedPacket {
|
||||
if isRegularPacket {
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber)
|
||||
}
|
||||
@@ -653,7 +714,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
||||
}
|
||||
if packetLost {
|
||||
pnSpace.history.DeclareLost(p.PacketNumber)
|
||||
if !p.skippedPacket {
|
||||
if isRegularPacket {
|
||||
// the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted
|
||||
h.removeFromBytesInFlight(p)
|
||||
h.queueFramesForRetransmission(p)
|
||||
@@ -665,12 +726,16 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
defer h.setLossDetectionTimer()
|
||||
func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error {
|
||||
defer h.setLossDetectionTimer(now)
|
||||
|
||||
if h.handshakeConfirmed {
|
||||
h.detectLostPathProbes(now)
|
||||
}
|
||||
|
||||
earliestLossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !earliestLossTime.IsZero() {
|
||||
if h.logger.Debug() {
|
||||
@@ -680,13 +745,14 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
h.tracer.LossTimerExpired(logging.TimerTypeACK, encLevel)
|
||||
}
|
||||
// Early retransmit or time loss detection
|
||||
return h.detectLostPackets(time.Now(), encLevel)
|
||||
h.detectLostPackets(now, encLevel)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTO
|
||||
// When all outstanding are acknowledged, the alarm is canceled in
|
||||
// setLossDetectionTimer. This doesn't reset the timer in the session though.
|
||||
// When OnAlarm is called, we therefore need to make sure that there are
|
||||
// When all outstanding are acknowledged, the alarm is canceled in setLossDetectionTimer.
|
||||
// However, there's no way to reset the timer in the connection.
|
||||
// When OnLossDetectionTimeout is called, we therefore need to make sure that there are
|
||||
// actually packets outstanding.
|
||||
if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation {
|
||||
h.ptoCount++
|
||||
@@ -701,11 +767,12 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, encLevel, ok := h.getPTOTimeAndSpace()
|
||||
if !ok {
|
||||
ptoTime, encLevel := h.getPTOTimeAndSpace(now)
|
||||
if ptoTime.IsZero() {
|
||||
return nil
|
||||
}
|
||||
if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation {
|
||||
ps := h.getPacketNumberSpace(encLevel)
|
||||
if !ps.history.HasOutstandingPackets() && !ps.history.HasOutstandingPathProbes() && !h.peerCompletedAddressValidation {
|
||||
return nil
|
||||
}
|
||||
h.ptoCount++
|
||||
@@ -739,7 +806,7 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetLossDetectionTimeout() time.Time {
|
||||
return h.alarm
|
||||
return h.alarm.Time
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ECNMode(isShortHeaderPacket bool) protocol.ECN {
|
||||
@@ -756,7 +823,7 @@ func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel)
|
||||
pnSpace := h.getPacketNumberSpace(encLevel)
|
||||
pn := pnSpace.pns.Peek()
|
||||
// See section 17.1 of RFC 9000.
|
||||
return pn, protocol.GetPacketNumberLengthForHeader(pn, pnSpace.largestAcked)
|
||||
return pn, protocol.PacketNumberLengthForHeader(pn, pnSpace.largestAcked)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber {
|
||||
@@ -864,33 +931,30 @@ func (h *sentPacketHandler) queueFramesForRetransmission(p *packet) {
|
||||
p.Frames = nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ResetForRetry(now time.Time) error {
|
||||
func (h *sentPacketHandler) ResetForRetry(now time.Time) {
|
||||
h.bytesInFlight = 0
|
||||
var firstPacketSendTime time.Time
|
||||
h.initialPackets.history.Iterate(func(p *packet) (bool, error) {
|
||||
for p := range h.initialPackets.history.Packets() {
|
||||
if firstPacketSendTime.IsZero() {
|
||||
firstPacketSendTime = p.SendTime
|
||||
}
|
||||
if p.declaredLost || p.skippedPacket {
|
||||
return true, nil
|
||||
}
|
||||
h.queueFramesForRetransmission(p)
|
||||
return true, nil
|
||||
})
|
||||
// All application data packets sent at this point are 0-RTT packets.
|
||||
// In the case of a Retry, we can assume that the server dropped all of them.
|
||||
h.appDataPackets.history.Iterate(func(p *packet) (bool, error) {
|
||||
if !p.declaredLost && !p.skippedPacket {
|
||||
h.queueFramesForRetransmission(p)
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
// All application data packets sent at this point are 0-RTT packets.
|
||||
// In the case of a Retry, we can assume that the server dropped all of them.
|
||||
for p := range h.appDataPackets.history.Packets() {
|
||||
if !p.declaredLost && !p.skippedPacket {
|
||||
h.queueFramesForRetransmission(p)
|
||||
}
|
||||
}
|
||||
|
||||
// Only use the Retry to estimate the RTT if we didn't send any retransmission for the Initial.
|
||||
// Otherwise, we don't know which Initial the Retry was sent in response to.
|
||||
if h.ptoCount == 0 {
|
||||
// Don't set the RTT to a value lower than 5ms here.
|
||||
h.rttStats.UpdateRTT(max(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now)
|
||||
h.rttStats.UpdateRTT(max(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0)
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
|
||||
}
|
||||
@@ -901,28 +965,36 @@ func (h *sentPacketHandler) ResetForRetry(now time.Time) error {
|
||||
h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Peek(), false)
|
||||
h.appDataPackets = newPacketNumberSpace(h.appDataPackets.pns.Peek(), true)
|
||||
oldAlarm := h.alarm
|
||||
h.alarm = time.Time{}
|
||||
h.alarm = alarmTimer{}
|
||||
if h.tracer != nil {
|
||||
if h.tracer.UpdatedPTOCount != nil {
|
||||
h.tracer.UpdatedPTOCount(0)
|
||||
}
|
||||
if !oldAlarm.IsZero() && h.tracer.LossTimerCanceled != nil {
|
||||
if !oldAlarm.Time.IsZero() && h.tracer.LossTimerCanceled != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
h.ptoCount = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SetHandshakeConfirmed() {
|
||||
if h.initialPackets != nil {
|
||||
panic("didn't drop initial correctly")
|
||||
func (h *sentPacketHandler) MigratedPath(now time.Time, initialMaxDatagramSize protocol.ByteCount) {
|
||||
h.rttStats.ResetForPathMigration()
|
||||
for p := range h.appDataPackets.history.Packets() {
|
||||
h.appDataPackets.history.DeclareLost(p.PacketNumber)
|
||||
if !p.skippedPacket && !p.isPathProbePacket {
|
||||
h.removeFromBytesInFlight(p)
|
||||
h.queueFramesForRetransmission(p)
|
||||
}
|
||||
}
|
||||
if h.handshakePackets != nil {
|
||||
panic("didn't drop handshake correctly")
|
||||
for p := range h.appDataPackets.history.PathProbes() {
|
||||
h.appDataPackets.history.RemovePathProbe(p.PacketNumber)
|
||||
}
|
||||
h.handshakeConfirmed = true
|
||||
// We don't send PTOs for application data packets before the handshake completes.
|
||||
// Make sure the timer is armed now, if necessary.
|
||||
h.setLossDetectionTimer()
|
||||
h.congestion = congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
h.rttStats,
|
||||
initialMaxDatagramSize,
|
||||
true, // use Reno
|
||||
h.tracer,
|
||||
)
|
||||
h.setLossDetectionTimer(now)
|
||||
}
|
||||
|
||||
93
vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go
generated
vendored
93
vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go
generated
vendored
@@ -2,23 +2,30 @@ package ackhandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
type sentPacketHistory struct {
|
||||
packets []*packet
|
||||
packets []*packet
|
||||
pathProbePackets []*packet
|
||||
|
||||
numOutstanding int
|
||||
|
||||
highestPacketNumber protocol.PacketNumber
|
||||
}
|
||||
|
||||
func newSentPacketHistory() *sentPacketHistory {
|
||||
return &sentPacketHistory{
|
||||
packets: make([]*packet, 0, 32),
|
||||
func newSentPacketHistory(isAppData bool) *sentPacketHistory {
|
||||
h := &sentPacketHistory{
|
||||
highestPacketNumber: protocol.InvalidPacketNumber,
|
||||
}
|
||||
if isAppData {
|
||||
h.packets = make([]*packet, 0, 32)
|
||||
} else {
|
||||
h.packets = make([]*packet, 0, 6)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *sentPacketHistory) checkSequentialPacketNumberUse(pn protocol.PacketNumber) {
|
||||
@@ -27,11 +34,11 @@ func (h *sentPacketHistory) checkSequentialPacketNumberUse(pn protocol.PacketNum
|
||||
panic("non-sequential packet number use")
|
||||
}
|
||||
}
|
||||
h.highestPacketNumber = pn
|
||||
}
|
||||
|
||||
func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) {
|
||||
h.checkSequentialPacketNumberUse(pn)
|
||||
h.highestPacketNumber = pn
|
||||
h.packets = append(h.packets, &packet{
|
||||
PacketNumber: pn,
|
||||
skippedPacket: true,
|
||||
@@ -40,7 +47,6 @@ func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) {
|
||||
|
||||
func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber) {
|
||||
h.checkSequentialPacketNumberUse(pn)
|
||||
h.highestPacketNumber = pn
|
||||
if len(h.packets) > 0 {
|
||||
h.packets = append(h.packets, nil)
|
||||
}
|
||||
@@ -48,28 +54,42 @@ func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber)
|
||||
|
||||
func (h *sentPacketHistory) SentAckElicitingPacket(p *packet) {
|
||||
h.checkSequentialPacketNumberUse(p.PacketNumber)
|
||||
h.highestPacketNumber = p.PacketNumber
|
||||
h.packets = append(h.packets, p)
|
||||
if p.outstanding() {
|
||||
h.numOutstanding++
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate iterates through all packets.
|
||||
func (h *sentPacketHistory) Iterate(cb func(*packet) (cont bool, err error)) error {
|
||||
for _, p := range h.packets {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
cont, err := cb(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cont {
|
||||
return nil
|
||||
func (h *sentPacketHistory) SentPathProbePacket(p *packet) {
|
||||
h.checkSequentialPacketNumberUse(p.PacketNumber)
|
||||
h.packets = append(h.packets, &packet{
|
||||
PacketNumber: p.PacketNumber,
|
||||
isPathProbePacket: true,
|
||||
})
|
||||
h.pathProbePackets = append(h.pathProbePackets, p)
|
||||
}
|
||||
|
||||
func (h *sentPacketHistory) Packets() iter.Seq[*packet] {
|
||||
return func(yield func(*packet) bool) {
|
||||
for _, p := range h.packets {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
if !yield(p) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHistory) PathProbes() iter.Seq[*packet] {
|
||||
return func(yield func(*packet) bool) {
|
||||
for _, p := range h.pathProbePackets {
|
||||
if !yield(p) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirstOutstanding returns the first outstanding packet.
|
||||
@@ -85,6 +105,14 @@ func (h *sentPacketHistory) FirstOutstanding() *packet {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirstOutstandingPathProbe returns the first outstanding path probe packet
|
||||
func (h *sentPacketHistory) FirstOutstandingPathProbe() *packet {
|
||||
if len(h.pathProbePackets) == 0 {
|
||||
return nil
|
||||
}
|
||||
return h.pathProbePackets[0]
|
||||
}
|
||||
|
||||
func (h *sentPacketHistory) Len() int {
|
||||
return len(h.packets)
|
||||
}
|
||||
@@ -120,6 +148,27 @@ func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePathProbe removes a path probe packet.
|
||||
// It scales O(N), but that's ok, since we don't expect to send many path probe packets.
|
||||
// It is not valid to call this function in IteratePathProbes.
|
||||
func (h *sentPacketHistory) RemovePathProbe(pn protocol.PacketNumber) *packet {
|
||||
var packetToDelete *packet
|
||||
idx := -1
|
||||
for i, p := range h.pathProbePackets {
|
||||
if p.PacketNumber == pn {
|
||||
packetToDelete = p
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx != -1 {
|
||||
// don't use slices.Delete, because it zeros the deleted element
|
||||
copy(h.pathProbePackets[idx:], h.pathProbePackets[idx+1:])
|
||||
h.pathProbePackets = h.pathProbePackets[:len(h.pathProbePackets)-1]
|
||||
}
|
||||
return packetToDelete
|
||||
}
|
||||
|
||||
// getIndex gets the index of packet p in the packets slice.
|
||||
func (h *sentPacketHistory) getIndex(p protocol.PacketNumber) (int, bool) {
|
||||
if len(h.packets) == 0 {
|
||||
@@ -140,6 +189,10 @@ func (h *sentPacketHistory) HasOutstandingPackets() bool {
|
||||
return h.numOutstanding > 0
|
||||
}
|
||||
|
||||
func (h *sentPacketHistory) HasOutstandingPathProbes() bool {
|
||||
return len(h.pathProbePackets) > 0
|
||||
}
|
||||
|
||||
// delete all nil entries at the beginning of the packets slice
|
||||
func (h *sentPacketHistory) cleanupStart() {
|
||||
for i, p := range h.packets {
|
||||
|
||||
16
vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go
generated
vendored
16
vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go
generated
vendored
@@ -36,7 +36,7 @@ type baseFlowController struct {
|
||||
// For every offset, it only returns true once.
|
||||
// If it is blocked, the offset is returned.
|
||||
func (c *baseFlowController) IsNewlyBlocked() (bool, protocol.ByteCount) {
|
||||
if c.sendWindowSize() != 0 || c.sendWindow == c.lastBlockedAt {
|
||||
if c.SendWindowSize() != 0 || c.sendWindow == c.lastBlockedAt {
|
||||
return false, 0
|
||||
}
|
||||
c.lastBlockedAt = c.sendWindow
|
||||
@@ -56,7 +56,7 @@ func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) (update
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *baseFlowController) sendWindowSize() protocol.ByteCount {
|
||||
func (c *baseFlowController) SendWindowSize() protocol.ByteCount {
|
||||
// this only happens during connection establishment, when data is sent before we receive the peer's transport parameters
|
||||
if c.bytesSent > c.sendWindow {
|
||||
return 0
|
||||
@@ -66,11 +66,6 @@ func (c *baseFlowController) sendWindowSize() protocol.ByteCount {
|
||||
|
||||
// needs to be called with locked mutex
|
||||
func (c *baseFlowController) addBytesRead(n protocol.ByteCount) {
|
||||
// pretend we sent a WindowUpdate when reading the first byte
|
||||
// this way auto-tuning of the window size already works for the first WindowUpdate
|
||||
if c.bytesRead == 0 {
|
||||
c.startNewAutoTuningEpoch(time.Now())
|
||||
}
|
||||
c.bytesRead += n
|
||||
}
|
||||
|
||||
@@ -82,19 +77,19 @@ func (c *baseFlowController) hasWindowUpdate() bool {
|
||||
|
||||
// getWindowUpdate updates the receive window, if necessary
|
||||
// it returns the new offset
|
||||
func (c *baseFlowController) getWindowUpdate() protocol.ByteCount {
|
||||
func (c *baseFlowController) getWindowUpdate(now time.Time) protocol.ByteCount {
|
||||
if !c.hasWindowUpdate() {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.maybeAdjustWindowSize()
|
||||
c.maybeAdjustWindowSize(now)
|
||||
c.receiveWindow = c.bytesRead + c.receiveWindowSize
|
||||
return c.receiveWindow
|
||||
}
|
||||
|
||||
// maybeAdjustWindowSize increases the receiveWindowSize if we're sending updates too often.
|
||||
// For details about auto-tuning, see https://docs.google.com/document/d/1SExkMmGiz8VYzV3s9E35JQlJ73vhzCekKkDi85F1qCE/edit?usp=sharing.
|
||||
func (c *baseFlowController) maybeAdjustWindowSize() {
|
||||
func (c *baseFlowController) maybeAdjustWindowSize(now time.Time) {
|
||||
bytesReadInEpoch := c.bytesRead - c.epochStartOffset
|
||||
// don't do anything if less than half the window has been consumed
|
||||
if bytesReadInEpoch <= c.receiveWindowSize/2 {
|
||||
@@ -106,7 +101,6 @@ func (c *baseFlowController) maybeAdjustWindowSize() {
|
||||
}
|
||||
|
||||
fraction := float64(bytesReadInEpoch) / float64(c.receiveWindowSize)
|
||||
now := time.Now()
|
||||
if now.Sub(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) {
|
||||
// window is consumed too fast, try to increase the window size
|
||||
newSize := min(2*c.receiveWindowSize, c.maxReceiveWindowSize)
|
||||
|
||||
63
vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go
generated
vendored
63
vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go
generated
vendored
@@ -12,8 +12,6 @@ import (
|
||||
|
||||
type connectionFlowController struct {
|
||||
baseFlowController
|
||||
|
||||
queueWindowUpdate func()
|
||||
}
|
||||
|
||||
var _ ConnectionFlowController = &connectionFlowController{}
|
||||
@@ -23,11 +21,10 @@ var _ ConnectionFlowController = &connectionFlowController{}
|
||||
func NewConnectionFlowController(
|
||||
receiveWindow protocol.ByteCount,
|
||||
maxReceiveWindow protocol.ByteCount,
|
||||
queueWindowUpdate func(),
|
||||
allowWindowIncrease func(size protocol.ByteCount) bool,
|
||||
rttStats *utils.RTTStats,
|
||||
logger utils.Logger,
|
||||
) ConnectionFlowController {
|
||||
) *connectionFlowController {
|
||||
return &connectionFlowController{
|
||||
baseFlowController: baseFlowController{
|
||||
rttStats: rttStats,
|
||||
@@ -37,20 +34,20 @@ func NewConnectionFlowController(
|
||||
allowWindowIncrease: allowWindowIncrease,
|
||||
logger: logger,
|
||||
},
|
||||
queueWindowUpdate: queueWindowUpdate,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connectionFlowController) SendWindowSize() protocol.ByteCount {
|
||||
return c.baseFlowController.sendWindowSize()
|
||||
}
|
||||
|
||||
// IncrementHighestReceived adds an increment to the highestReceived value
|
||||
func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount) error {
|
||||
func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount, now time.Time) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
// If this is the first frame received on this connection, start flow-control auto-tuning.
|
||||
if c.highestReceived == 0 {
|
||||
c.startNewAutoTuningEpoch(now)
|
||||
}
|
||||
c.highestReceived += increment
|
||||
|
||||
if c.checkFlowControlViolation() {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FlowControlError,
|
||||
@@ -60,44 +57,47 @@ func (c *connectionFlowController) IncrementHighestReceived(increment protocol.B
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) {
|
||||
func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) (hasWindowUpdate bool) {
|
||||
c.mutex.Lock()
|
||||
c.baseFlowController.addBytesRead(n)
|
||||
shouldQueueWindowUpdate := c.hasWindowUpdate()
|
||||
c.mutex.Unlock()
|
||||
if shouldQueueWindowUpdate {
|
||||
c.queueWindowUpdate()
|
||||
}
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.addBytesRead(n)
|
||||
return c.hasWindowUpdate()
|
||||
}
|
||||
|
||||
func (c *connectionFlowController) GetWindowUpdate() protocol.ByteCount {
|
||||
func (c *connectionFlowController) GetWindowUpdate(now time.Time) protocol.ByteCount {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
oldWindowSize := c.receiveWindowSize
|
||||
offset := c.baseFlowController.getWindowUpdate()
|
||||
if oldWindowSize < c.receiveWindowSize {
|
||||
offset := c.getWindowUpdate(now)
|
||||
if c.logger.Debug() && oldWindowSize < c.receiveWindowSize {
|
||||
c.logger.Debugf("Increasing receive flow control window for the connection to %d kB", c.receiveWindowSize/(1<<10))
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
return offset
|
||||
}
|
||||
|
||||
// EnsureMinimumWindowSize sets a minimum window size
|
||||
// it should make sure that the connection-level window is increased when a stream-level window grows
|
||||
func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount) {
|
||||
func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount, now time.Time) {
|
||||
c.mutex.Lock()
|
||||
if inc > c.receiveWindowSize {
|
||||
c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10))
|
||||
newSize := min(inc, c.maxReceiveWindowSize)
|
||||
if delta := newSize - c.receiveWindowSize; delta > 0 && c.allowWindowIncrease(delta) {
|
||||
c.receiveWindowSize = newSize
|
||||
}
|
||||
c.startNewAutoTuningEpoch(time.Now())
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if inc <= c.receiveWindowSize {
|
||||
return
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
newSize := min(inc, c.maxReceiveWindowSize)
|
||||
if delta := newSize - c.receiveWindowSize; delta > 0 && c.allowWindowIncrease(delta) {
|
||||
c.receiveWindowSize = newSize
|
||||
if c.logger.Debug() {
|
||||
c.logger.Debugf("Increasing receive flow control window for the connection to %d, in response to stream flow control window increase", newSize)
|
||||
}
|
||||
}
|
||||
c.startNewAutoTuningEpoch(now)
|
||||
}
|
||||
|
||||
// Reset rests the flow controller. This happens when 0-RTT is rejected.
|
||||
// All stream data is invalidated, it's if we had never opened a stream and never sent any data.
|
||||
// All stream data is invalidated, it's as if we had never opened a stream and never sent any data.
|
||||
// At that point, we only have sent stream data, but we didn't have the keys to open 1-RTT keys yet.
|
||||
func (c *connectionFlowController) Reset() error {
|
||||
c.mutex.Lock()
|
||||
@@ -108,5 +108,6 @@ func (c *connectionFlowController) Reset() error {
|
||||
}
|
||||
c.bytesSent = 0
|
||||
c.lastBlockedAt = 0
|
||||
c.sendWindow = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user